Any crate that allows taking from and placing back into a mutable reference?

Hi, I’m wondering if any crate already offers a function like this:

use scopeguard::*;
use std::*;

pub fn modify<T>(x: &mut T, f: impl FnOnce(T) -> T) {
    defer_on_unwind!{
        process::abort();
    }
    unsafe {
        ptr::write(x, f(ptr::read(x)))
    }
}

Also, I’m pretty sure this is sound, but not 100%.

https://docs.rs/take_mut/0.2.2/take_mut/

1 Like

Interesting, now that I’m seeing it, I feel like I have actually read the docs of this crate before. :laughing:

Hmm, that's implemented with catch_unwind; I thought there was a newer one that did it by conditionally panicking in Drop. (To take advantage of the built-in panic-while-unwinding-aborts behaviour.)

That would be replace_with.

2 Likes

What about the simplest approach?

pub fn modify<T> (x: &'_ mut T, f: impl FnOnce(T) -> T)
{
    let abort_on_drop =
        ::scopeguard::guard((), |()| ::std::process::abort())
    ;
    unsafe {
        ::core::ptr::write(x, f(::core::ptr::read(x)))
    }
    ::core::mem::forget(abort_on_drop);
}

The only issue is that it requires std, so for #![no_std] crates, it would need to have a panic-within-panic replacement.

Seems like this is pretty much exactly what replace_with::replace_with_or_abort ends up doing.

but not exactly

now I’ve looked into replace_with it enough to find out that it leaks memory: (miri confirms)
(Edit: it was not a memory leak, but miri isn’t happy nontheless)

Edit2: I’m not seeing any UB anymore in the replace_with crate but either their source or Miri has to be wrong. ⟶ for anyone interested