Can I get a Rust array's length with only a type, not a concrete variable?

纵然是瞬间 提交于 2020-12-06 07:06:52

问题


I want to rewrite the following C++ code into Rust:

using storage = array<int, 3>;
const size_t storage_len = sizeof(storage) / sizeof(storage::value_type);

How can I get that constant length value without a concrete variable?

As motivation, although it may seem trivial, I want to print the array's element count without declaring a variable. I know I could use a constant value or declare a dummy variable, but I wonder how Rust can preserve C++ code.

I admit without a concrete variable is not clear. I want to achieve the above C++ feature, but this explanation can be misleading. I'm curious if there is any way to get the array's element type.


回答1:


I understand that you want to retrieve the array length from the type information alone. Rust does not have built-in PI types (a.k.a. const generics). This means generic parameters which are not types (like an integer for an array length) are currently not supported by the language.

There is an issue tracking this and we are likely to see support for it in the future, though not the near future.

If you have to, you can work around that limitation by implementing a trait for each type:

trait GetLength {
    fn len() -> usize;
}

impl<T> GetLength for [T; 0] {
    fn len() -> usize {
        0
    }
}

impl<T> GetLength for [T; 1] {
    fn len() -> usize {
        1
    }
}

// ...

fn main() {
    println!("{}", <[String; 1]>::len());
}

Macros can help prevent repetitive typing:

trait GetLength {
    fn len() -> usize;
}

macro_rules! impl_get_length {
    ($v:expr) => {
        impl<T> GetLength for [T; $v] {
            fn len() -> usize {
                $v
            }
        }
    };
}

impl_get_length!{ 0 }
impl_get_length!{ 1 }

// ...

fn main() {
    println!("{}", <[String; 1]>::len());
}

Crates like typenum also help to provide some support for const generics within the existing language.




回答2:


In Rust, you can get the size of a type with std::mem::size_of, so you can get the length of your array type the same way as in C++:

use std::mem::size_of;

type Storage = [i32; 3];

fn main() {
    println!("Length: {}", size_of::<Storage>() / size_of::<i32>());
}

playground

However this requires knowing the type of items stored in the array. I don't know of a way to get that without instantiating a variable.




回答3:


Just for fun:

use std::mem;
use std::ops::Deref;

fn main() {
    assert_eq!(5, num_elems::<[i32; 5]>());
}

fn num_elems<T>() -> usize 
where 
    T: 'static, 
    &'static T: IntoIterator,
    <&'static T as IntoIterator>::Item: Deref,
    <<&'static T as IntoIterator>::Item as Deref>::Target: Sized,
{
    fn inner<S, I>() -> usize 
    where 
        I: Deref,
        <I as Deref>::Target: Sized,
    { 
        mem::size_of::<S>() / mem::size_of::<I::Target>()
    }

    inner::<T, <&'static T as IntoIterator>::Item>()
}

This will work for any array up to 32 elements, and will panic if the array element type is zero-sized. Also, you can use other things, besides array types, and I have no idea what it will do.




回答4:


You can use mem::size_of:

let storage_len = std::mem::size_of::<[i32; 3]>() / std::mem::size_of::<i32>();



回答5:


Arrays coerce to slices, so any method available on slices are also available on arrays. Like len():

let v = [0u32; 128];
assert_eq!(128, v.len());


来源:https://stackoverflow.com/questions/52379284/can-i-get-a-rust-arrays-length-with-only-a-type-not-a-concrete-variable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!