Crates.io | const_guards_attribute |
lib.rs | const_guards_attribute |
version | 0.1.3 |
source | src |
created_at | 2022-05-03 14:00:46.704875 |
updated_at | 2022-07-10 17:04:30.600836 |
description | An attribute macro for compile time constraints on const generics |
homepage | https://github.com/Mari-W/const_guards |
repository | https://github.com/Mari-W/const_guards |
max_upload_size | |
id | 579770 |
size | 15,521 |
With const_guards
you can express certain compile time constraints on rust's const_generics
using the unstable generic_const_exprs
feature.
For documentation visit docs.rs.
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.
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!()
}