jeiea
jeiea

Reputation: 2405

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

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.

Upvotes: 7

Views: 4528

Answers (5)

Markus Klein
Markus Klein

Reputation: 1376

Rust now has support for const generics. I left the old answer so people have an idea why someone might have asked this in the first place.

New Answer since Rust 1.51:

pub trait Length {
    const LEN: usize;
}

impl<T, const LENGTH: usize> Length for [T; LENGTH] {
    const LEN: usize = LENGTH;
}

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

Old Answer:

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.

Upvotes: 13

Peter Hall
Peter Hall

Reputation: 58735

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.

Upvotes: 3

Jmb
Jmb

Reputation: 23264

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.

Upvotes: 7

Gr&#233;gory OBANOS
Gr&#233;gory OBANOS

Reputation: 1051

You can use mem::size_of:

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

Upvotes: 2

udoprog
udoprog

Reputation: 1865

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());

Upvotes: -1

Related Questions