Static Assertions 1.0

3 minute read

The static_assertions Rust library is now 1.0!

This means quite a lot of things. Check out CHANGELOG.md and the 1.0 docs for detailed info.

Thank you

First of all, I would like to thank you (yes, you). Thank you for being part of the community that’s used this library and given me feedback and suggestions for it.

At RustConf 2019, it wasn’t uncommon for me to introduce myself as the creator of static_assertions and subsequently be told how productive and confident it’s made people. Hearing that over the course of the conference brought me immense joy.

This community is the reason that I will be able to attend RustFest Barcelona this year through my GoFundMe.

❤️

What is Static Assertions?

static_assertions is a library designed to enable users to perform various checks at compile-time. It allows for finding errors quickly and early when it comes to ensuring certain features or aspects of a codebase. The macros it provides are especially important when exposing a public API that requires types to be the same size or implement certain traits.

No more labels

With Rust 1.37 introducing const _, the labeling requirement has been completely removed! 🎉

Prior to this, every global macro needed a label to avoid conflicts:

const_assert!(val1; true);
const_assert!(val2; 1 < 2);

Now it’s as simple as:

const_assert!(true);
const_assert!(42 < 9000);

Many thanks to @joshlf for the RFC and to @Centril for pushing this feature forward.

Asserting equal type alignment

As of 1.0, it is now possible to assert that two types are equal in alignment via assert_eq_align!.

struct Foo(*const u8);

assert_eq_align!(Foo, *mut u8, usize);

The macro is defined as:

macro_rules! assert_eq_align {
    ($x:ty, $($xs:ty),+ $(,)?) => {
        const _: fn() = || {
            use $crate::_core::mem::align_of;
            $(let _: [(); align_of::<$x>()] = [(); align_of::<$xs>()];)+
        };
    };
}

This ensures correct alignment by making sure that the declared array type ([(); align_of::<$x>()]) and the array instances ([(); align_of::<$xs>()]) match in size.

New syntax for some macros

The assert_fields! and assert_impl_* family of macros are now called with a colon instead of a comma to separate out the type.

struct Foo {
    bar: // ...
    baz: // ...
}

assert_fields!(Foo: bar, baz);

trait Bar {}
trait Baz {}

impl Bar for Foo {}
impl Baz for Foo {}

assert_impl_all!(Foo: Bar, Baz);

Asserting !Trait, a.k.a. negative trait bounds

As of 0.3.4, it is possible to assert that a given type does not implement all or any in a given set of traits via assert_not_impl_all! and assert_not_impl_any! respectively. This is thanks to @HeroicKatora via #17.

The following example fails to compile since u32 can be converted into u64:

assert_not_impl_any!(u32: Into<u64>);

The following compiles because Cell<u32> is neither Sync nor Send:

use std::cell::Cell;

assert_not_impl_all!(Cell<u32>: Sync, Send);

Going Forward

There are a number of features that are not yet implemented that I think would be possible.

Advanced constant evaluation

Today, by using the -Zunleach-the-miri-inside-of-you flag, you can do if, loops, and other control flow in a const context. My friend @oli-obk mentioned the possibilities enabled with this during his RustConf 2019 talk @ 22:54.

Once if in const is stabilized (see issue), const_assert! will go from having its awkward “subtraction with overflow” error message to simply panic!-ing with a proper error message.

assert_impl_any!

There should exist a macro that asserts a given type implements any of a given set of traits. This is being tracked at #19.

struct Foo;

trait Bar {}
trait Baz {}

assert_impl_any!(Foo: Bar, Baz);

Until Foo implements Bar or Baz, this program would fail to compile.

Mutually exclusive trait implementations

By leveraging assert_impl_any! in combination with assert_not_impl_all!, you can ensure that a type implements one of two traits but not both. This is being tracked at #20.

struct Foo;

trait Bar {}
trait Baz {}

impl Bar for Foo {}

// Uncommenting the following line results in a compile failure
// impl Baz for Foo {}

assert_impl_any!(Foo: Bar, Baz);
assert_not_impl_all!(Foo: Bar, Baz);