Is use-after-forget valid?

Is this code valid? Does Rust guarantee that memory is still valid (for its lifetime) after std::mem::forget?

unsafe {
    let src = s.as_ptr();
    let mut tmp: [T; 32] = std::mem::uninitialized();
    let t = tmp.as_mut_ptr();
    std::mem::forget(tmp);
    std::ptr::copy_nonoverlapping(src, t, 32);
    // add code that may panic
}

The reason for wanting to do this is to ensure that tmp is not dropped if a panic occurs.

Assuming it is valid, what is the purpose of the NoDrop structure at https://github.com/rust-lang/rust/blob/134c4a0f08a3d1f55ea8968fbe728fa935c71698/src/libcollections/slice.rs#L1380 which uses a union to avoid dropping? Is this just a different way to achieve the same thing, or does it provide some different capabiity?

No, this code is invalid. The tmp variable is moved into the forget function. It doesn't matter whether this function is forget or something else. The variable has been moved out, so tmp is considered dead after the call and the compiler is free to use this chunk of stack memory for something else.

What is actually forgotten is the memory (or other resources) owned by T here. So for example, if tmp: [Vec<u8>; 32], the u8s owned by each vector could be used after forget. But in your case, you're using unintialized here, so it doesn't really matter.

NoDrop is really what you want here, but if you want a stable solution, you can consider:

  • Using Vec<T> – you can forget/call into_ptr on this,
  • Using [u8; 32 * sizeof::<T>()] (I don't think it would compile, unless you know the size beforehand)
  • Using some struct that wraps Option<[T; 32]> and in destructor of this struct, take and forget the array.
  • Catching the panic and forget the array there.
5 Likes

BTW, if you're interested in NoDrop, nagisa's ManuallyDrop RFC was recently merged, which will provide equivalent functionality (once through its stabilization period, of course).