Skip to content

Add macro static_assert to perform compile-time assertion checking #325

Closed
@chirsz-ever

Description

@chirsz-ever

Proposal

Problem statement

People often want to check some conditions at compile-time, but currently Rust does not have an intuitionistic way to do it.

Motivating examples or use cases

Here are use cases in rust-lang/rust and Rust-for-Linux:

https://github.com/rust-lang/rust/blob/432fffa8afb8fcfe658e6548e5e8f10ad2001329/library/std/src/io/error/repr_bitpacked.rs#L352

https://github.com/rust-lang/rust/blob/432fffa8afb8fcfe658e6548e5e8f10ad2001329/compiler/rustc_index/src/lib.rs#L43

https://github.com/rust-lang/rust/blob/432fffa8afb8fcfe658e6548e5e8f10ad2001329/compiler/rustc_middle/src/ty/consts/kind.rs#L75

https://github.com/Rust-for-Linux/linux/blob/cae4454fe293141be428436e5278261494cef02a/rust/kernel/static_assert.rs

People define their own versions of static_assert macros to meet the requirements.

Solution sketch

Add a new macro static_assert which check it's argument in compile-time.

static_assert is allowed to used at module's top-level, not like assert which can only used in a function or initialization context:

// allowed
static_assert!(0 == 0);

// not allowed: error: non-item macro in item position
assert!(0 == 0);

Like assert and debug_assert, static_assert_eq and static_assert_ne could also be added to the standard library.

The error message for failed assertion would be like:

error[E0796]: static assertion failed
 --> src/lib.rs:7:1
  |
7 | static_assert!(0 == 1);
  | ^^^^^^^^^^^^^^^^^^^^^^^ static assertion failed: 0 == 1

The macro can take a custom error message like assert, but currently we cannot call non-const formatting macro in constants. My opinion is just keeping the behavior same as assert, in the future when formating is allowd in const context, we would automaticly get the feature.

The macro should be able to be used in generic context to assert about generic parameters:

fn foo<const N: usize>() {
    static_assert!(N < 2);
}

Maybe some drawbacks:

  • Adding macros to prelude is a break change. But the todo, dbg and matches were added to prelude, maybe this is not a big problem?
  • Maybe we could use another name, such as const_assert. I think static_assert is better, becaus C, C++ and rustc developers just used it.

Alternatives

There are several alternatives, the precondition is that const_panic is stablized since Rust 1.57, so we could perform assertion at const context, which would cause a compile-time error.

We can write const _: () = assert!(condition). This is stable, and works for most cases. The drawback is that it is not intuitionistic with grammar noise. People need to lean them from somewhere, such as TRPL or Rust Cookbook.

Another drawback of const _: () = assert!(condition) is that, due to E0401, you cannot assert about generic parameters:

fn foo<const N: usize>() {
    const _: () = assert!(N < 1);
}

playground

This code cannot be compiled currently, so Rust-For-Liunx has to use a build_assert macro.

With const block stable since 1.79, we could write const { assert!(condition) } to perform static assertion. It has no problem with generic parameters:

#![feature(inline_const)]

pub fn foo<const N: usize>() {
    const { assert!(N < 1); };
}

pub fn bar() {
    foo::<0>();
    // foo::<1>();
}

playground

But the const block has another issue: it is an expression so it is not able to be written at top level. I think it is easy to define the top-level const block:

const {
    do_sth();
}

is identical to

const _: () = {
    do_sth();
};

Links and related work

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions