Applying by-value function to &mut T


#1

Picture something like this, perhaps in std::mem. It takes a function that transforms something “by value” and applies it in-place to a mutable borrow.

fn apply<T,F>(dest: &mut T, func: F)
where F: FnOnce(T) -> T,
{
    // sprinkle unsafety or compiler fairy dust to do something
    // like the following, but without invoking clone():
    let x = dest.clone();
    *dest = func(x);
}
  • Does this exist somewhere in core/std?
  • What level of hackery is/would be required to write it? (safe rust, unsafe, compiler magic…)
  • Is it even sound?

#2

It’s not sound. Imagine what happens when func panics and there’s a destructor somewhere that will look at that &mut T location.

There’s a crate that allows you to do that, which makes each panic an abort for the duration of the function func.

Fortunately, most types provide some empty default state, that can be used as a temporary replacement. If the function is short and can’t panic the whole replacement will probably be elided by llvm.

*dest = func(mem::replace(dest, Default::default()));

#3

It’s not sound. Imagine what happens when func panics and there’s a destructor somewhere that will look at that &mut T location.

Interesting, so there is a good reason.

Come to think of it, I imagine very similar problems would exist for “placement in,” and must have been addressed as part of the proposal. This may be naive, but I wonder if some sort of usage of the “placement in” interface could make it sound?

Edit: s/usage of/adherence to/

Fortunately, most types provide some empty default state, that can be used as a temporary replacement. If the function is short and can’t panic the whole replacement will probably be elided by llvm

This sounds like a good idea, thanks!


#4

Come to think of it, I imagine very similar problems would exist for “placement in,”

Yeah, panic is why the “placement in” semantics are so convoluted. Without it we could have &in pointers which are uninitialised and have to been written to and then have a method Vec<T>::place(&mut self) -> &in T. Then you could write:

my_vec.place() *= foo;

This would also solve your problem because you would be able to move out of a &mut T and temporarily turn it into an &in T.


#5

wheeeeee