Nicer static assertions

As of Rust 1.57, now that const_panic is stable it's now possible to do the following:

pub const FOO: usize = 42;
pub const BAR: usize = 42;

// Flagged as dead code unfortunately
#[allow(dead_code)]
const fn assert_foo_equals_bar() {
    assert!(FOO == BAR);
}

const _: () = assert_foo_equals_bar();

...and that's pretty cool and a nice workaround to enforce some properties which are checked at compile time to the point that if something's wrong you get a compile error:

error[E0080]: evaluation of constant value failed
 --> src/lib.rs:6:5
  |
6 |     assert!(FOO == BAR);
  |     ^^^^^^^^^^^^^^^^^^^
  |     |
  |     the evaluated program panicked at 'assertion failed: FOO == BAR', src/lib.rs:6:5
  |     inside `assert_foo_equals_bar` at src/lib.rs:6:5
...
9 | const _: () = assert_foo_equals_bar();
  |               ----------------------- inside `_` at src/lib.rs:9:15

But what would really be nice is this (which I believe is closer to what C++'s static_assert provides):

pub const FOO: usize = 42;
pub const BAR: usize = 42;

// Or potentially `assert!` if it's possible 
static_assert!(FOO == BAR);

Has anyone proposed this before, and if so, is there a relevant tracking issue?

It's easy enough to write (not quite complete, but the basic idea):

macro_rules! static_assert {
    ($cond:expr) => {
        #[allow(dead_code)]
        const fn static_assertion() {
            assert!($cond);
        }

        const _: () = static_assertion();
    }
}

...but this is one of those things that's tiny enough and fundamental enough that I don't really want to pull in an external crate to define it, but annoying boilerplate to copy from crate-to-crate, especially if I just want to use it once.

8 Likes

You can already just write

const _: () = assert!(FOO == BAR, "assert_foo_equals_bar");

with no need for the function.

There's no way for a macro to know if it's used in expression or item position (and there shouldn't be), so the only way to make bare assert! work would be to make statements in item position evaluated at const time. This is probably not the best idea, as it's likely to be confused with script-style execution.

It probably is worth adding a const_assert! to the stdlib, as

macro_rules! const_assert {
    ($($tt:tt)*) => {
        const _: () = assert!($($tt)*);
    }
}

(perhaps with assert!'s match arms instead of a pass-through catch-all).

Just for linking's sake, here's the crate that does so (with pre-const-panic methods):

15 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.