How to create a Box<UnsafeCell<[T]>>

Viewed 159

The recommended way to create a regular boxed slice (i.e. Box<[T]>) seems to be to first create a std::Vec<T>, and use .into_boxed_slice(). However, nothing similar to this seems to work if I want the slice to be wrapped in UnsafeCell.

A solution with unsafe code is fine, but I'd really like to avoid having to manually manage the memory.

2 Answers

The only (not-unsafe) way to create a Box<[T]> is via Box::from, given a &[T] as the parameter. This is because [T] is ?Sized and can't be passed a parameter. This in turn effectively requires T: Copy, because T has to be copied from behind the reference into the new Box. But UnsafeCell is not Copy, regardless if T is. Discussion about making UnsafeCell Copy has been going on for years, yielding no final conclusion, due to safety concerns.

If you really, really want a Box<UnsafeCell<[T]>>, there are only two ways:

  • Because Box and UnsafeCell are both CoerceUnsize, and [T; N] is Unsize, you can create a Box<UnsafeCell<[T; N]>> and coerce it to a Box<UnsafeCell<[T]>. This limits you to initializing from fixed-sized arrays.

Unsize coercion:

fn main() {
    use std::cell::UnsafeCell;
    let x: [u8;3] = [1,2,3];
    let c: Box<UnsafeCell<[_]>> = Box::new(UnsafeCell::new(x));
}
  • Because UnsafeCell is #[repr(transparent)], you can create a Box<[T]> and unsafely mutate it to a Box<UnsafeCell<[T]>, as the UnsafeCell<[T]> is guaranteed to have the same memory layout as a [T], given that [T] doesn't use niche-values (even if T does).

Transmute:

// enclose the transmute in a function accepting and returning proper type-pairs
fn into_boxed_unsafecell<T>(inp: Box<[T]>) -> Box<UnsafeCell<[T]>> {
    unsafe {
        mem::transmute(inp)
    }
}

fn main() {
    let x = vec![1,2,3];
    let b = x.into_boxed_slice();
    let c: Box<UnsafeCell<[_]>> = into_boxed_unsafecell(b);
}

Having said all this: I strongly suggest you are suffering from the xy-problem. A Box<UnsafeCell<[T]>> is a very strange type (especially compared to UnsafeCell<Box<[T]>>). You may want to give details on what you are trying to accomplish with such a type.

Just swap the pointer types to UnsafeCell<Box<[T]>>:

use std::cell::UnsafeCell;


fn main() {
    let mut res: UnsafeCell<Box<[u32]>> = UnsafeCell::new(vec![1, 2, 3, 4, 5].into_boxed_slice());
    unsafe {
        println!("{}", (*res.get())[1]);
        res.get_mut()[1] = 10;
        println!("{}", (*res.get())[1]);    
    }
}

Playground

Related