rust-lang / libs-team

The home of the library team
Apache License 2.0
124 stars 19 forks source link

Add Vec::last_or_push and friends #465

Open balt-dev opened 1 day ago

balt-dev commented 1 day ago

Proposal

Problem statement

Getting a reference to the value you just pushed to a vector requires an extra call and unwrap:

let mut v = Vec::new();
v.push(42);
*v.last_mut().unwrap() = 43;

Motivating examples or use cases

I'm currently porting https://github.com/Naruyoko/OmegaNum.js/ to Rust, and this is a common pattern all over: x.array[i+1]=(x.array[i+1]||0)+1; This is directly translated as

if x.is_empty() {
    x.push(0);
}
let last_mut = x.last_mut().unwrap();
*last_mut += 1;

With this, this could be the following:

*x.last_mut_or_push(0) += 1;

which is much nicer.

Solution sketch

Add functions for Vec::last_or_push(T) -> &T, Vec::last_mut_or_push(T) -> &mut T, Vec::last_or_push_with(impl FnOnce() -> T) -> &T, Vec::last_mut_or_push_with(impl FnOnce() -> T) -> &mut T, which would get the last element of the vector, or push a value and return a reference to that.

Alternatives

Alternatively, Vec::push could return &mut T, which would remove the possiblity of a panic - maybe it would be better, but both would be nice. See issue #464.

balt-dev commented 1 day ago

I'm willing to make a PR with this if everyone's okay with these changes as-is.

balt-dev commented 1 day ago

Note: It might be a good idea to have first_or_push and friends as well.

programmerjake commented 1 day ago

I think having a Vec::push equivalent that returns a reference to the just-pushed value is independently useful, maybe name it Vec::push_and_get?

balt-dev commented 1 day ago

At that point, why not have Vec::push just return &mut T (barring any concerns over the breaking change)? This is what #464 was about, but I closed it in favor of this because I don't want to focus on two ACPs at once. I might reopen it after this one is settled if it's independently useful like you say, but I'd need a better example for it.