Casting an array

    pub fn foo<const M: usize>(&self, arr: [T; M]) {
        match M {
            0 => {},
            1 => {
                let x: [T; 1] = ... ;

            }
            2 => {
                let x: [T; 2] = ...;
            }
            _ = {}
        }
    }

How do I cast this array ? I tried using as [T; 1] but it complains about being a non-primitive caast.

Hmm, I don't know how to do that safely. I had hoped that this would work:

pub fn foo<T, const M: usize>(arr: [T; M]) {
    match arr {
        x @ [] => {},
        x @ [_] => {

        }
        x @ [_,_] => {
        }
        _ => {}
    }
}
error[E0308]: mismatched types
 --> src/lib.rs:3:13
  |
3 |         x @ [] => {},
  |             ^^ expected `0`, found `M`
  |
  = note: expected array `[T; 0]`
             found array `[T; M]`

error[E0308]: mismatched types
 --> src/lib.rs:4:13
  |
4 |         x @ [_] => {
  |             ^^^ expected `1`, found `M`
  |
  = note: expected array `[T; 1]`
             found array `[T; M]`

error[E0308]: mismatched types
 --> src/lib.rs:7:13
  |
7 |         x @ [_,_] => {
  |             ^^^^^ expected `2`, found `M`
  |
  = note: expected array `[T; 2]`
             found array `[T; M]`

but it doesn't.

Do you need actual ownership, or is an &[T; 2] enough?

The way I understand generics is: the code has to be valid no matter what the generic parameter actually is. So all branches of the code have to be valid for any arbitrary M... which is impossible when you're trying to cast to specific array sizes.

Assuming you genuinely only have a limited number of cases, I'd define a trait that does what you want, and implement it for just those sizes. Something like:

trait Blah {}
impl<T> Blah for [T; 0] {}
impl<T> Blah for [T; 1] {}
impl<T> Blah for [T; 2] {}

I... don't know how you'd handle the fallback case of other values of N, though.

1 Like

I need actual ownership. The context is: passing arguments to the toy rc_allocator. If we pass by ref, (I think) I'll need T to be Clone, whereas if we can pass it as [T; 1] or [T; 2] it should work fine.

I can't think of any way other than a transmute, or other unsafe operations that are equivalent to a transmute.

This will work:

use core::mem::transmute_copy;
use core::mem::ManuallyDrop;

pub fn transmute_array<T, const FROM: usize, const TO: usize>(array: [T; FROM]) -> [T; TO] {
    assert_eq!(FROM, TO);

    let arr = ManuallyDrop::new(array);
    // SAFETY: The assert ensures that `FROM == TO`.
    unsafe { transmute_copy::<[T; FROM], [T; TO]>(&*arr) }
}

Use it like this:

let x: [T; 1] = transmute_array(arr);
2 Likes
    pub fn foo<const M: usize>(&self, arr: [T; M]) {
        match M {
            0 => {}
            1 => {
                let x: [T; 1] = unsafe { mem::transmute::<_, [T; 1]>(arr) };
            }
            2 => {
                let x: [T; 2] = unsafe { mem::transmute::<_, [T; 2]>(arr) };
            }
            _ => {}
        }
    }


60 |                 let x: [T; 1] = unsafe { mem::transmute::<_, [T; 1]>(arr) };
   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: source type: `[T; M]` (this type does not have a fixed size)
   = note: target type: `[T; 1]` (size can vary because of T)


How would you transmute

Using the unstable unsized_locals I came up with this:

#![allow(incomplete_features)]
#![feature(unsized_locals)]

pub fn foo<T, const M: usize>(arr: [T; M]) {
    let arr: Box<[T]> = Box::new(arr);
    match *arr {
        x @ [] => {
            
        },
        x @ [_] => {
            
        }
        x @ [_,_,_] => {
            
        }
        _ => {}
    }
}

Right. If you allocate, then it is probably possible without unsafe or unstable. You can use this to convert from Box<[T]> to Box<[T; M]>:

impl<T, const N: usize> TryFrom<Box<[T]>> for Box<[T; N]> {
    type Error = Box<[T]>;

    /// Attempts to convert a `Box<[T]>` into a `Box<[T; N]>`.
    ///
    /// The conversion occurs in-place and does not require a
    /// new memory allocation.
    ///
    /// # Errors
    ///
    /// Returns the old `Box<[T]>` in the `Err` variant if
    /// `boxed_slice.len()` does not equal `N`.
    fn try_from(boxed_slice: Box<[T]>) -> Result<Self, Self::Error> {
        if boxed_slice.len() == N {
            Ok(unsafe { boxed_slice_as_array_unchecked(boxed_slice) })
        } else {
            Err(boxed_slice)
        }
    }
}
1 Like

I happened to see a RFC Pattern types RFC · GitHub for it just now, in which it seems to advocate the exact syntax IIUC.
I got the link from a zulipchat thread from @Jules-Bertholet , but after searching for a while, the pre-RFC discussion is here.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.