How to parameterize a struct's array size?

I would like to parameterize a structs array size.

I tried with trait constants in following code, but have now read that they don't work for arrays. Is there a suggested workaround? Do I need to use a macro for this?

trait Trait {
    const SIZE: usize;
}

struct Big;

impl Trait for Big {
    const SIZE: usize = 100;
}

struct Small;

impl Trait for Small {
    const SIZE: usize = 10;
}

struct Container<T: Trait> {
    array: [u8; T::SIZE],
}

impl<T: Trait> Container<T> {
    fn new(size: T) -> Self {
        Container {
            array: [0; T::SIZE]
        }
    }
}

fn main() {
    let b = Container::new::<Big>();
    let s = Container::new::<Small>();
}


(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0599]: no associated item named `SIZE` found for type `T` in the current scope
  --> src/main.rs:18:20
   |
18 |     array: [u8; T::SIZE],
   |                    ^^^^ associated item not found in `T`
   |
   = help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `SIZE`, perhaps you need to restrict type parameter `T` with it:
   |
17 | struct Container<T: Trait + Trait> {
   |                  ^^^^^^^^^^

error[E0392]: parameter `T` is never used
  --> src/main.rs:17:18
   |
17 | struct Container<T: Trait> {
   |                  ^ unused parameter
   |
   = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0392, E0599.
For more information about an error, try `rustc --explain E0392`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

2 Likes

Stable Rust doesn't support const generics yet, so this is not possible. Without const generics size of an array is not a regular constant, but a special hack that doesn't work in such scenarios.

4 Likes

For this reason there are many crates that introduce an Array trait and impl it for a fixed list of sizes with a macro. See e.g. arrayvec::Array.

3 Likes

Thanks again, solved it for me.

If anyone needs this in future here's a final solution based on SmallVec that does what I want.

use smallvec::{SmallVec, Array};

trait Spec {
    type Array: Array<Item = Self::Item>;
    type Item: Clone;
}

#[derive(Clone)]
struct Container<T: Spec> {
    chunk: SmallVec<T::Array>,
}

impl<T: Spec> Container<T> {
    fn new() -> Self {
        Container {
            chunk: SmallVec::new()
        }
    }

    fn as_slice(&self) -> &[T::Item] {
        self.chunk.as_ref()
    }
}

struct Big;

impl Spec for Big {
    type Array = [u8; 64];
    type Item = u8;
}

struct Small;

impl Spec for Small {
    type Array = [u8; 128];
    type Item = u8;
}

fn test() {
    let big: Container<Big> = Container::new();
    let small: Container<Small> = Container::new();
}

(Playground)

1 Like