Limit the size of a const array?

Hello! Is there any way to limit the size of an array constructed using const generics? I would like to create a const array and have guarantees that it will not exceed a specific length. Ideally I would be able to say something like this:

struct Foo<const Size: u16>([u8; Size as usize]);

but that doesn't seem to work. Something like compile-time assertions would also be sufficient, and I've looked into const_assert! from the static_assertions crate, but using that desugars to

struct Foo<const Size: usize>([u8; Size]);

impl<const Size: usize> Foo<Size> {
    const fn new() -> Self {
        // const_assert!(Size <= 65_535);
        const _: [
            ();
            0 - !{
                const ASSERT: bool = Size <= 65_535;
                ASSERT
            } as usize
        ] = [];
        Self([0; Size])
    }
}

which throws an error that I can't use generic parameters from outer item. a const is a separate item from the item that contains it.

Is there any way in stable to achieve this goal of confirming that Size fits in a u16 without having to assert! so on every access at runtime, or will I need to wait for something like generic_const_exprs to be stabilized?

struct Foo<const Size: usize>([u8; Size]);

impl<const Size: usize> Foo<Size> {
    pub fn new() -> Self {
        const {
            assert!(Size <= 50);
        }
        Foo([0; Size])
    }
}

fn main() {
    //Foo::<60>::new();
    
    Foo::<40>::new();
}

If you uncomment the first line in main it won't compile anymore. You can see the const block so that part will be evaluated at compile time. If you do something in there that can't be done at compile time it will give an error.

5 Likes

So the problem with using const_assert! was that it declares a variable rather than being a standalone block with no output? I'm baffled that the solution ends up being so simple! Thank you!

I honestly don't really know what the issue with const_assert is, but the const block feature is pretty new (Rust 1.79 from June 13) so the crate was developed way before that and was probably more useful before that became stable.

It looks like const_assert! is running into a limitation of const items, which after some digging (thanks for letting me know this feature was added in 1.79!) is explained in the const blocks section of the "Block expressions" page in the Rust Book.

1 Like

Note that there's an important difference between const items and const blocks here: because const items are non-generic, we guarantee that they always run. So they're better for global checks: const _: () = assert!(usize::BITS == 33); will always fail compilation, but const { assert!(usize::BITS == 33) } is only guaranteed to fail compilation if the item it's in is "used".

2 Likes

Thank you for the clarification! In this case that's fine because the check isn't global, but it's good to keep in mind for the future.

1 Like

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.