This tracks the stabilization of this function added by PR https://github.com/rust-lang/rust/pull/61130 in std::mem:
pub fn take<T: Default>(dest: &mut T) -> T {
replace(dest, T::default())
}
Original feature request:
I decently often find myself in a position where I have a HashMap, Vec, or some other non-Copy type I want to "take" from its current location, but cannot because I only have &mut self. A particularly common place for this to happen is when finally returning Async::Ready from Future::poll. Usually in these cases, I end up writing:
return Async::Ready(std::mem::replace(self.result, Vec::new()));
This works pretty well, since Vec::new does not allocate (as is the case for most collections).
This pattern is common enough that it'd be nice if there was a more standard way to do it. In particular, something like:
mod std::mem {
fn take<T: Default>(t: &mut T) -> T {
std::mem::replace(t, Default::default())
}
}
A variant of this was suggested back in #33564, though that modified Default instead, which seems like overkill.
If this is something others agree would be useful, I'd be happy to submit a PR.
EDIT: Changed name from take to swap_default.
EDIT: Changed name back to take.
Seems like a reasonable addition to me 馃憤
I've written this helper in projects before as replace_default() which is clearer but is more verbose.
Implemented in https://github.com/rust-lang/rust/pull/61130
mem::replace
mem::swap also works.
That's what C++ did before moves were introduced, because "taking" the value and leaving something cheap but valid instead is exactly that.
"Something cheap but valid" is not necessarily Default, so in C++ it's leaved to decide to the type itself and Rust analogue would be something like
trait Steal {
fn steal(&mut self) -> Self;
}
Default implementations for T: Copy and T: Default types are possible, except they will probably conflict without specialization.
mem::swapalso works.
@petrochenkov not sure what you mean? I don't think you can do this with mem::swap without separately producing the value in the caller?
As for adding a trait, we _could_ totally do that, though Default seemed like a decent candidate for filler value to me. Can you think of types where you would _not_ want Default, but there is some _other_ acceptable filler value?
@jonhoo
Can you think of types where you would not want Default, but there is some other acceptable filler value?
Trivial example - for any Copy type it's faster to just memcpy it than e.g. memcpy + reset the origin to default value.
Can't give a non-copying example right away, need to think.
for any
Copytype it's faster to just memcpy it than e.g. memcpy + reset the origin to default value.
Hopefully that assignment will be eliminated by the optimizer
The implementation PR https://github.com/rust-lang/rust/pull/61130 is landing, I鈥檝e edited the issue description and labels to make it a tracking issue.
At some point, we should revert commit ac21b60, which doesn't currently build. (This will convert compiletest usage of replace to take).
^This is tracked in #62306. It should be done once this is stabilized.
@jonhoo would you like to send a stabilization PR? I feel like this is pretty uncontroversial and useful, to the point that I first type mem::take and than recall that it is not stable yet :-)
@matklad done in #64716
Stabilized in https://github.com/rust-lang/rust/pull/64716.
Most helpful comment
@jonhoo would you like to send a stabilization PR? I feel like this is pretty uncontroversial and useful, to the point that I first type
mem::takeand than recall that it is not stable yet :-)