#const #generics

macro const_guards_attribute

An attribute macro for compile time constraints on const generics

4 releases

Uses new Rust 2021

0.1.3 Jul 10, 2022
0.1.2 May 3, 2022
0.1.1 May 3, 2022
0.1.0 May 3, 2022

#553 in Development tools

Download history 23/week @ 2022-04-27 71/week @ 2022-05-04 54/week @ 2022-05-11 16/week @ 2022-05-18 12/week @ 2022-05-25 29/week @ 2022-06-01 14/week @ 2022-06-08 9/week @ 2022-06-15 8/week @ 2022-06-22 6/week @ 2022-06-29 66/week @ 2022-07-06 35/week @ 2022-07-13 16/week @ 2022-07-20 24/week @ 2022-07-27 13/week @ 2022-08-03

91 downloads per month
Used in 2 crates (via const_guards)

MIT license

15KB
298 lines

Const Guards [docs.rs]

With const_guards you can express certain compile time constraints on rust's const_generics using the unstable generic_const_exprs feature.

Documentation

For documentation visit docs.rs.

Motivation

Consider the following usage of the first method on arrays from the standard library:

let array: [(); 1] = [(); 1];
let head: Option<&()> = array.first();

Would it be nice if we could just write

let head: &() = array.first();

since the compiler should know this array has length 1 at this point. With const guards we can express such as follows:

#[guard(N > 0)]
fn first<'a, T, const N: usize>(array: &'a [T; N]) -> &'a T {
    &array[0]
}

The index call on the array &array[0] cannot possible fail because we enforced the length of the array to be > 0 at compile time. We could now call it as follows

let array: [(); 1] = [(); 1];
let head: &() = first(&array);

while the case where the array is actually empty would fail to compile:

let array: [(); 0] = [(); 0];
let head: &() = first(&array);

Finally we could even express this as a trait to make it more accessable:

trait ArrayHead<T, const N: usize> {
    #[guard(<const N: usize> { N > 0 })]
    fn head(&self) -> &T;
}

impl<T, const N: usize> ArrayHead<T, N> for [T; N] {
    fn head(&self) -> &T {
        &self[0]
    }
}

fn main() {
    let array: &[(); 1] = &[(); 1];
    let head: &() = array.head();
}

Though, as you can see, we need to introduce generics not introduced by the guarded item explicitly.

Implementation

Consider this simple example of a const guard:

fn main() {
    f::<0>()
}

#[guard(N > 0)]
fn f<const N: usize>() {
    todo!()
}

and have a look at the expanded form:

struct Guard<const U: bool>;

trait Protect {}
impl Protect for Guard<true> {}

fn main() {
    f::<0>()
}

fn f<const N: usize>()
where
    Guard<{
        const fn _f_guard<const N: usize>() -> bool {
            if !N > 0 {
                panic!("guard evaluated to false")
            }
            true
        }
        _f_guard::<N>()
    }>: Protect,
{
    todo!()
}

Todo

  • Improve error messages
  • Write more tests

Dependencies

~215–620KB
~15K SLoC