#const-generics #const #generics #compile-time

const_guards

用于对 const 泛型的编译时约束的属性宏

3 个版本

0.1.3 2022 年 7 月 10 日
0.1.2 2022 年 5 月 3 日
0.1.1 2022 年 5 月 3 日

#1185开发工具

每月 33 次下载
constrained_int 中使用

MIT 许可证

12KB

Const Guards [docs.rs]

使用 const_guards,您可以使用不稳定特性 generic_const_exprs 来表达 Rust 的 const_generics 的某些编译时约束。

文档

有关文档,请访问 docs.rs

动机

考虑以下标准库中数组对 first 方法的使用

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

如果我们能够直接写出

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

将会很方便,因为编译器应该知道在这个点数组长度为 1。使用 const guards 我们可以表达如下

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

由于我们在编译时强制了数组的长度必须大于 0,所以对数组的索引调用 &array[0] 不可能失败。现在我们可以这样调用它

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

而数组实际为空的情况将 无法编译

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

我们甚至可以将它表达为一个 trait,使其更易于访问

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();
}

尽管如此,如您所见,我们需要显式引入受保护项未引入的泛型。

实现

考虑以下 const guards 的简单示例

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

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

并查看其展开形式

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!()
}

待办事项

  • 改进错误信息
  • 编写更多测试

依赖项

~1.5MB
~35K SLoC