在 1.51 版本中稳定了 const 泛型 MVP 之后,const 泛型项目组继续致力于 const 泛型的工作。这项工作的大部分内容都由特性门 `const_generics` 和 `const_evaluatable_checked` 保护。随着时间的推移,`const_generics` 特性本身变得相当无用,而 `const_evaluatable_checked` 的名称并没有真正捕捉到这个特性的预期用途。
为了改进这一点,我们最近移除了 `const_generics`、`lazy_normalization_consts` 和 `const_evaluatable_checked` 特性。它们已被 `feature(adt_const_params)` 和 `feature(generic_const_exprs)` 取代。
由于 const 泛型有很多内容,这里快速概述一下新的和已有的特性,以及它们要稳定还需要做多少工作
feature(adt_const_params)
在稳定版中,只允许整数、`char` 和 `bool` 作为 const 参数的类型。此特性允许其他类型,例如 `&'static str` 和用户定义的类型。
#![feature(adt_const_params)]
#[derive(PartialEq, Eq)]
enum ImageFormat {
Rgb8,
Rgba8,
// ...c
}
struct Image<const FORMAT: ImageFormat> {
// ...
}
impl Image<{ ImageFormat::Rgba }> {
fn alpha(&self, pixel: PixelLocation) -> u8 {
// ...
}
}
请注意,即使有了此特性,泛型 const 参数类型,例如 `struct Foo<T, const N: T> { ... }`,也是禁止的。虽然希望允许这样做,但这会增加超出我们当前能力的额外复杂性。
稳定化仍然存在两个主要障碍
第一个是向 valtrees 的过渡。Valtrees 是将值表示为带有整数节点的树,简化了我们与更复杂类型的交互方式。
此外,我们还必须弄清楚我们*甚至想*允许哪些类型作为 const 参数类型。这与关于“结构匹配”的讨论相关,该讨论仍在进行中。
虽然上面提到的问题绝对不是微不足道的,但它绝对有可能在几个月内为稳定化做好准备。
feature(generic_const_exprs)
在没有任何不稳定的特性下,const 参数必须是一个完全具体的表达式或一个泛型参数本身,因此像 `N + 1` 这样的常量是被禁止的。有了这个特性,可以使用泛型参数的表达式。
#![feature(generic_const_exprs)]
fn split_first<T, const N: usize>(arr: [T; N]) -> (T, [T; N - 1]) {
// ...
}
struct BitSet<const SIZE: usize>
where
[u8; (SIZE + 7) / 8]: Sized,
{
storage: [u8; (SIZE + 7) / 8],
}
我们目前要求用户添加断言泛型常量成功求值的边界。对于项的 API 中可见的所有常量,这些边界是隐式添加的。
如果类型为 `Foo` 的常量表达式 `expr` 在其他情况下不会在 `where` 子句或函数签名中使用,我们会在项的 `where` 子句中添加一个提及 `expr` 的不相关的边界。为此,可以定义 `struct Evaluatable<const N: Foo>;` 并使用 `Evaluatable<{ expr }>: ` 作为边界。如果 `expr` 的类型为 `usize`,我们倾向于为此使用 `[u8; expr]:` 或 `[u8; expr]: Sized`。虽然我们很可能会在将来为此类边界添加专用语法,但我们会在该特性的其余部分更成熟之前等待。
此特性距离稳定版还很远,并且存在一些重大未解决的问题。特别是对于 `where` 边界内的常量,我们必须修复许多微妙的错误和向后不兼容问题,然后才能考虑如何稳定化它。
feature(const_generics_defaults)
与类型参数默认值类似,此特性增加了为 const 参数声明默认值的能力。
#![feature(const_generics_defaults)]
struct ArrayStorage<T, const N: usize = 2> {
arr: [T; N],
}
impl<T> ArrayStorage<T> {
fn new(a: T, b: T) -> ArrayStorage<T> {
ArrayStorage {
arr: [a, b],
}
}
}
为了允许类型参数默认值与 const 参数在同一列表中,我们还打算取消类型和 const 参数的排序限制,允许 `struct Foo<const N: usize, T = [u32; N]> { ... }`。
此特性几乎已准备好稳定化,目前受阻于找出稳定化报告的任何潜在边缘情况。
feature(generic_arg_infer)
虽然已经可以在主体内部使用通配符 `_` 作为类型参数,但对于 const 参数而言并非如此。此特性为常量添加了此功能。
#![feature(generic_arg_infer)]
fn array_from<T, U, const N: usize>(arr: [T; N]) -> [U; N]
where
U: From<T>,
{
arr.map(From::from)
}
fn main() {
let x = ["this", "is", "a", "six", "element", "array"];
// using `_` for the parameter `N` lets
// the compiler infer the correct value
let _y = array_from::<_, String, _>(x);
}
此特性尚未准备好稳定化,尽管此处没有任何已知的大型障碍。要自信地稳定它,我们可能需要进行一些大型重构,因为目前的设置在某些方面感觉相当脆弱。