我正在开展一个项目,在 Rust 中构建一个位向量类型,并且正在探索是否可以使用不等式界限来约束常量泛型。
我的目标是设计一个 API,通过使用基于 const 泛型的编译时保证以及运行时检查的变体来避免运行时边界检查。我希望这些函数足够简单,以便编译器可以内联它们,从而实现真正的零成本抽象。
下面是一个简化的代码示例,演示了我想要做的事情。这段代码无法编译,但它显示了我想要的约束类型:
/// Idea: A function with multiple generic parameters where a
/// trait bound is used to establish an ordering between them.
fn add_ordered<const N1: usize, const N2: usize>() -> usize
where N2 > N1 {
N1 + N2
}
fn main() {
let n = add_ordered::<1, 2>();
println!("{n}");
}
如果我删除 where N2 > N1 子句,代码可以编译,但它不会支持我在这里试图保留的不变量类型(在本例中,N2 大于 N2)。
我的问题:
- Rust 中目前是否有办法编写类似的 const 泛型约束
where N2 > N1
? - 如果没有,是否有任何解决方法或语言功能(夜间或其他)可以实现类似功能?
- 如果现在真的没有办法做到这一点,除了还没实现之外,还有什么理由不能实现吗?这会不会成为潜在的 RFC?
我搜索了一些资料,但没找到太多相关信息。如有任何指点或解释,我将不胜感激。谢谢!
为什么这段代码不能编译
where
Rust 中的子句参与类型系统——编译器试图在编译时证明任何类型、特征、方法等的使用都符合其where
子句,如果不符合,则拒绝该程序。除了限制何时可以使用代码之外,
where
子句还提供了一个假设,以便编译器在证明程序正确性时使用。例如,以下假设函数无法编译:因为编译器无法证明调用的条件
t.clone()
得到满足。但是,如果你添加一个合适的where
子句:那么代码现在可以正确编译,因为编译器可以使用该子句作为调用有效的
where
证明。t.clone()
你正在编写的代码的一个基本问题是,如果你编写了一个
where
子句,那么你实际上是在告诉 Rust 使用它来检查你定义的函数的调用是否正确,以及你定义的函数的调用是否正确。Rust 目前不支持这种对常量参数可能值的推理——例如,如果该语法被接受,程序员可能会期望能够使用where A > B
andwhere B > C
子句的组合来满足where A > C
需求,这意味着需要有人实现能够推理>
运算符相关属性的代码。事实上,即使是最简单的情况也尚未实现:
where N1 == N2
也被拒绝(错误消息“where
子句中尚不支持相等约束”,并链接到Rust 问题 #20041)。事实证明,即使是相当简单的约束,在类型系统证明器中实现也很困难。一个很好的思考方式是,为了编译泛型,Rust 编译器基本上需要充当定理证明器,并生成编译正确的证明;并且,任何可以对参与证明的泛型施加的额外约束都需要实现为定理证明器能够处理的内容,这通常非常困难(在一般情况下可能是不可能的)。您尝试使用的特定语法存在另一个问题:在
where
子句之后,Rust 通常期望看到类型的名称,并且出现在需要类型的位置,<
并且>
像括号一样工作并相互匹配。因此,Rust 将 解释>
为不匹配的右括号,而不是大于运算符,这就是为什么您收到的错误消息看起来令人困惑且毫无关联的原因。如果你不需要类型级证明
所有这些麻烦的发生,本质上是因为
where
这些子句既要求编译器在类型检查时证明某些内容,又要求类型检查器可以用它来证明某些内容。这很可能并非你真正想要的,你试图表达的要求实际上只是一个安全性/正确性要求,而不是参与类型级证明的内容——你希望编译器检查它,但并不关心它是否在类型检查器中被具体检查,也不关心它是否可以用作假设来证明其他内容。如果您希望某些内容在编译时进行检查,但不一定由类型检查器进行检查,则无需使用
where
关键字:相反,更通用的编译时求值关键字是const
。在较新的 Rust 版本中(1.79 或更高版本 - 1.79 于 2024 年 6 月 13 日发布,因此有些人仍在使用旧版本),您可以const
在函数主体内编写断言:这将在编译时进行检查,如果任何对
add_ordered
存在条件的调用N2 > N1
不成立,编译时将引发错误(错误消息会说明失败的条件、const { … }
代码块的位置以及函数调用的位置)。与where
子句不同,它不会参与类型检查;编译器会N2 > N1
根据您的要求进行验证,但除了在不成立时引发编译时错误外,不会将这些信息用于任何其他目的。希望这样做足以满足您的需求;对于类型级证明来说,这还不够好,但仍然足够好,例如,验证健全性不变量或捕获 API 的意外无意义的使用。
很遗憾,稳定版不支持此功能。
在 Nightly 版中,你可以这样表达:
(是的,语法很奇怪)
然而,该功能并不完善,甚至可能存在缺陷。
而且据我所知,短期内不太可能稳定下来。