In the spirit of take_mut
but for pinned values:
#![no_std]
use core::pin::Pin;
/// Drop a pinned value early, replacing it with another.
///
/// There's no use making `val: T` as there's a panic path that needs to be aborted in any case.
/// This allows the `Drop` to return some buffers _before_ the closure is invoked, i.e. the new
/// task might reuse some resources just returned by the old task.
pub fn replace_pin_with<T>(pin: Pin<&mut T>, val: impl FnOnce() -> T) {
struct AbortOnPanic;
impl Drop for AbortOnPanic {
fn drop(&mut self) {
// We only enter this on a panic, so this double panics, i.e. aborts.
panic!()
}
}
// Ensure our local panic unwind is unreachable. We temporarily leave objects invalid and must
// not return while in that state.
let _aborter = AbortOnPanic;
// Pointer to the inner value. Note: huh. Really this should be one operation with no
// intermediate, alias-asserting, mutable unique reference. However, there is no such defined
// operation in the standard library. Really curious.
let value = unsafe { pin.get_unchecked_mut() } as *mut _;
// Drop the value in-place, fulfilling the `Pin` contract early. Will return a valid value to
// the caller by refilling the slot just below.
unsafe { core::ptr::drop_in_place(value) };
let new_value = val();
// The caller guarantees that it will be dropped before the memory is reused. Se it's okay we
// pin this new value by writing it to pinned memory.
unsafe { core::ptr::write(value, new_value) };
// Success, no need to abort.
core::mem::forget(_aborter);
}
- Is this considered sound up to RFCs?
- Is this consistent with the current
noalias
assertions actually emitted forPin
? - Should it?