Rust: Tracking issue for std::mem::take

Created on 24 May 2019  路  13Comments  路  Source: rust-lang/rust

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.

B-unstable C-tracking-issue T-libs

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::take and than recall that it is not stable yet :-)

All 13 comments

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.

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::swap also 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 Copy type 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

Was this page helpful?
0 / 5 - 0 ratings