Expand description

const_guards is a simple attribute macro that makes it easy to express certain compile time constraints on const_generics using generic_const_exprs.

Example

#![feature(generic_const_exprs)]
use const_guards::guard;

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

Guards

Guards can have either a expression or a polymorphic block as argument.

Expression Guard

The expression guard can only use const generics and normal generics introduced by the item and is limited to one rust expression allowed in const fn.

#[guard(PREFIX == '_' || PREFIX == '#')]
struct Label<const PREFIX: char> (&'static str);
let label = Label::<'!'>("important");
let label = Label::<'_'>("unused");
Polymorphic Block Guard

The polymorphic block is denoted <const A: Type, const B: Type, .., T, V, ..> { .. } and can introduce const generics and normal generics from the outer scope to the block in curly brackets. The generics are optional and an polymorphic block without the need of outer generics can be denoted as { .. }

Inside the block the same kind of logic that can be used inside const fn is allowed.

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]
  }
}
  let array = &[(); 0];
  let head: &() = array.head();
  let array = &[(); 1];
  let head: &() = array.head();

Items

Guards are allowed to be an attribute of the following rust items:

  • fn
  • enum
  • struct
  • impl

Advanced

Custom Error Messages

Raise an custom error by panic!-ing inside the guard. This will be rendered as compile error with help of rust’s const_panic. With that in mind we could modify the trait definition from Polymorphic Block Guard.

trait ArrayHead<T, const N: usize> {
  #[guard(<const N: usize> {
    if N == 0 {
        panic!("expected at least one item in array")
    } else {
        true
    }
  })]
  fn head(&self) -> &T;
}
Const Functions

It’s possible to outsource logic by calling other const fn. With that in mind we could modify the trait definition from Polymorphic Block Guard.

const fn is_not_empty<const N: usize>() -> bool {
  N > 0
}

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

For an example for the expansion that takes places see [here] [here]: https://docs.rs/const_guards/latest/const_guards/attr.guard.html

Structs

Traits

Attribute Macros

Macro Expansion