3 个版本
0.1.3 | 2022 年 7 月 10 日 |
---|---|
0.1.2 | 2022 年 5 月 3 日 |
0.1.1 | 2022 年 5 月 3 日 |
#1185 在 开发工具 中
每月 33 次下载
在 constrained_int 中使用
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