Skip to content

Commit 2e02a9e

Browse files
committed
Added Box::take() method
The new `Box::take()` method allows to read from `Box` and reuse the allocation safely. This commit also changes some `unsafe` code in the compiler to use this method instead and becomes safe.
1 parent 4e8fb74 commit 2e02a9e

File tree

3 files changed

+59
-10
lines changed

3 files changed

+59
-10
lines changed

compiler/rustc_data_structures/src/functor.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,8 @@ impl<T> IdFunctor for Box<T> {
1717
where
1818
F: FnMut(Self::Inner) -> Result<Self::Inner, E>,
1919
{
20-
let raw = Box::into_raw(self);
21-
Ok(unsafe {
22-
// SAFETY: The raw pointer points to a valid value of type `T`.
23-
let value = raw.read();
24-
// SAFETY: Converts `Box<T>` to `Box<MaybeUninit<T>>` which is the
25-
// inverse of `Box::assume_init()` and should be safe.
26-
let raw: Box<mem::MaybeUninit<T>> = Box::from_raw(raw.cast());
27-
// SAFETY: Write the mapped value back into the `Box`.
28-
Box::write(raw, f(value)?)
29-
})
20+
let (value, allocation) = Box::take(self);
21+
Ok(Box::write(allocation, f(value)?))
3022
}
3123
}
3224

library/alloc/src/boxed.rs

+56
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,62 @@ impl<T, A: Allocator> Box<T, A> {
578578
{
579579
*boxed
580580
}
581+
582+
/// Safely reads the value on heap without deallocating.
583+
///
584+
/// This method is mostly useful for avoiding reallocations.
585+
/// The method consumes the box to prevent double-free.
586+
/// The value on heap should be considered truly uninitialized and MUST NOT be read.
587+
/// Specifically, this code is definitely UB:
588+
///
589+
/// ```no_run
590+
/// #![feature(new_uninit)]
591+
///
592+
/// let value = Box::new("hello".to_owned());
593+
/// // Double free here!
594+
/// unsafe { Box::take(value).1.assume_init(); }
595+
/// ```
596+
///
597+
/// # Examples
598+
///
599+
/// ```
600+
/// #![feature(new_uninit)]
601+
///
602+
/// let boxed_value = Box::new("hello".to_owned());
603+
/// let (mut value, allocation) = Box::take(boxed_value);
604+
/// assert_eq!(value, "hello");
605+
/// // More realistic code would consume the value and produce other value.
606+
/// // For simple demonstration we just modify it here.
607+
/// value.push_str(" world");
608+
/// let boxed_value = Box::write(allocation, value);
609+
/// assert_eq!(*boxed_value, "hello world");
610+
/// ```
611+
#[unstable(feature = "new_uninit", issue = "63291")]
612+
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
613+
#[inline]
614+
pub const fn take(this: Self) -> (T, Box<mem::MaybeUninit<T>, A>) {
615+
let (raw, alloc) = Box::into_raw_with_allocator(this);
616+
unsafe {
617+
// SAFETY:
618+
// * The pointer given to from_raw_in was obtained from box above using the same
619+
// allocator
620+
// * Casting `*mut T` to `*mut MaybeUninit<T>` is sound because they have same layout
621+
// * Safely obtaining `&mut T` pointing into the allocation will become impossible after
622+
// this call because we consume `this` (no variance issues)
623+
// * `MaybeUninit` disables destructor of `T` so it can't double free
624+
// * Leak shouldn't happen because `assume_init_read` below doesn't panic and the value
625+
// can be dropped afterwards
626+
let new_box = Box::from_raw_in(raw.cast::<mem::MaybeUninit<T>>(), alloc);
627+
// SAFETY:
628+
// * The value being read was proven to be initialized when this function was called
629+
// * We didn't touch the value inside this function, just cast the pointer, so it's
630+
// still valid.
631+
// * We don't read the value on heap again and prevent safe code from reading it by
632+
// marking the box with `MaybeUninit`.
633+
let value = new_box.assume_init_read();
634+
(value, new_box)
635+
}
636+
}
581637
}
582638

583639
impl<T> Box<[T]> {

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
#![feature(const_size_of_val)]
102102
#![feature(const_align_of_val)]
103103
#![feature(const_ptr_read)]
104+
#![feature(const_maybe_uninit_assume_init_read)]
104105
#![feature(const_maybe_uninit_write)]
105106
#![feature(const_maybe_uninit_as_mut_ptr)]
106107
#![feature(const_refs_to_cell)]

0 commit comments

Comments
 (0)