Transmute single value boxed array to boxed value

Greetings!

How can I use transmute single value boxed array to boxed value?

fn single<T>(arr: Box<[T]>) -> Option<Box<T>> {
    if arr.len() == 1 {
        // SAFETY: Just checked len.
        unsafe {
            let raw_arr = Box::into_raw(arr);
            let raw_value: *mut T = std::mem::transmute(raw_arr);
            let value: Box<T> = Box::from_raw(raw_value);
            Some(value)
        }
    } else {
        None
    }
}

fn main() {
    assert_eq!(Some(Box::new(123u32)), single(Box::from([123])));
    assert_eq!(None, single(Box::from([1, 2, 3])));
    assert_eq!(None, single(Box::<[u32]>::from([])));
}
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
 --> src/main.rs:6:37
  |
6 |             let raw_value: *mut T = std::mem::transmute(raw_arr);
  |                                     ^^^^^^^^^^^^^^^^^^^
  |
  = note: source type: `*mut [T]` (128 bits)
  = note: target type: `*mut T` (64 bits)

You shouldn't be transmuting pointers. Transmutation is not transitive – even if transmute::<T, U>() is valid, it doesn't imply that you can do the same thing by transmuting between &T and &U or *mut T and *mut U, neither is it implied the other way around.

The immediate problem in your code is that Box<[T]> is a fat pointer (since it has to store the length of the slice somewhere). Instead of going unsafe, you can (and should) use the standard TryFrom implementation between slices and arrays:

fn single<T>(slice: Box<[T]>) -> Option<Box<T>> {
    <Box<[T; 1]>>::try_from(slice)
        .map(|b| match *b { [item] => item.into() })
        .ok()
}

If you want to make sure that the allocation is kept and no new allocation takes place, then you can perform pointer casting after converting the fat pointer to a thin pointer. Also, use the .cast() method on raw pointers, its safer than as, since it can't cast away immutability:

fn single<T>(slice: Box<[T]>) -> Option<Box<T>> {
    <Box<[T; 1]>>::try_from(slice)
        .map(|b| unsafe { Box::from_raw(Box::into_raw(b).cast()) })
        .ok()
}

However, I would just omit the box from the the return type altogether, and return the item by-value instead:

fn single<T>(slice: Box<[T]>) -> Option<T> {
    <Box<[T; 1]>>::try_from(slice)
        .map(|b| match *b { [item] => item })
        .ok()
}
4 Likes