2020年的出货常量泛型

2020-07-16 23:27:31

很难相信,自从我开通RFC2000以来,已经有3年多的时间了,它定义了Rust的常量泛型。同时,阅读RFC线程,这一领域也发生了巨大的变化:首先,在编写RFC的时候,const FNS是不稳定的,而且const甚至还没有使用MIRI进行评估。多年来,关于const泛型特性已经做了很多工作,但是仍然没有发布。然而,我认为我们已经定义了一个非常有用的常量泛型子集,该子集足够稳定,可以在短期内发布。

对于那些不知道的人来说,常量泛型指的是常量值的泛型,而不是类型。这允许通过例如特定整数值来参数化类型。不稳定的Rust,只有特殊的数组类型-[T;N]-有一个常量参数(数组的长度),没有办法对所有的N值进行抽象,即使对于数组也是如此。常量泛型将允许创建由值参数化的新类型,以及实现在这些值上抽象的特征和函数。例如:

//这是一个自定义类型,由`usize`pub struct foo<;const N:usize>;{bytes:[U8;N],}参数化;//这是一个已经针对//任意长度的数组实现的特征。实施[T;N]的调试,常量N:usize>;调试(&L;T){}。

一年多来,我们一直使用常量泛型特性来实现数组的特征,人为地将实现限制在长度为32的数组上。我们计划在即将发布的版本中取消这一限制。通过稳定常量泛型,我们可以开始允许外部板条箱实现所有数组的特征,以及许多其他有趣的用例。然而,这将有两个重要的限制。

const泛型可以具有的初始类型将仅限于整数基元类型,即有符号和无符号整数类型、布尔值和字符。目前,不允许使用复合类型或用户定义的类型,也不允许引用(意味着也不允许使用字符串)。这是当前实现的一个相对容易修复的缺点,并且构造泛型的用户定义类型将在近期而不是更长的时间内成为可能。

我想提一下,因为很多人没有意识到这一点,即使撇开rustc目前的限制不谈,也不是任何类型都可以用作const参数。重要的是,为了使Rust的类型系统健全,两个类型之间的相等必须是确定性的、自反性的、对称的,等等。也就是说,如果您的自定义类型通过掷硬币来实现相等,则该类型的两个值有时相等,有时不相等。这意味着,由这些值参数化的两个类型有时是相同的类型,有时是不同的类型,这会给类型系统带来相当大的麻烦。

(作为一个具体的例子,浮点数带来了挑战,例如,foo<;nan>;不会被认为是与其本身相同的类型,因为NaN不等于它自己。)。

这个问题的长期解决方案是“结构相等”的概念,它是严格正确的EQ类型子集,满足在类型系统中使用所需的属性。在实现匹配时,Rust已经使用了此结构相等属性。如果为您的类型及其所有成员派生Eq和PartialEq,则它应该满足不匹配和常量泛型所需的结构相等属性。

另一个限制是只能使用两种表达式来填充常量泛型位置:

常量泛型参数。例如,在引入了常量泛型的Imp<;const N:usize>;中,可以按字面意思使用该值填充常量泛型。

可以在不依赖于任何类型或常量参数的常量上下文中使用的表达式。这意味着文字、数学公式、使用常量、调用常量FN等,只要不涉及自由泛型。

这限制了用户想要使用const泛型做的许多更奇特、更有趣的事情,例如,您不能将两个数组长度组合在一起以形成类型为[T;N+M]的数组,甚至不能使用[T;N*2]将数组长度加倍。基本上不能有依赖于计算的类型,而计算依赖于其他泛型。正确合理地实现这一行为确实是实现常量泛型的难点,虽然工作正在进行,但还没有准备好。

这也意味着常量泛型不能基于关联的类型、关联的常量或泛型方法填充。没有返回[U8;mem::size_of::<;T>;()]的函数,没有使用Self Like[U8;Self::Len]等。

这将意味着,例如,像加密散列特征这样的用例,允许每个实现为它们输出的散列指定不同的长度,仍然不在MVP的范围之内。这很可惜,但是这个功能最终还是会实现的。

所有这些限制听起来似乎功能相当有限,但我认为一旦用户能够使用它,就会发现很多真正强大的用例。首先,今天可能只由某些数组类型实现的特征,或者根本不实现的特征,将开始由所有数组实现,从而使数组成为语言中更为重要的一部分。我们已经找到了很棒的用例。请看此代码示例。

设data=[1,2,3,4,5,6];设sum1=data。array_chunks()。贴图(|&;[x,y]|x*y)。sum::<;I32>;();assert_eq!(sum1,(1*2)+(3*4)+(5*6));设sum2=数据。array_chunks()。贴图(|&;[x,y,z]|x*y*z)。sum::<;I32>;();assert_eq!(sum2,(1*2*3)+(4*5*6));

根据传递给map函数的模式,编译器计算出第一次调用array_chunks应该将数据分块到长度为2的数组迭代器中,而在第二次调用中应该是长度为3的数组迭代器。这太酷了!

现在我们已经弄清楚了在不久的将来我们可以稳定什么,我正在围绕稳定这个功能的愿景大肆宣传和达成共识-这就是这篇博客文章的目的!下一步将由某人创建一个功能门,即这组有限的const泛型(与现有的整个const_Generics功能门相对),然后让我们完成记录和稳定该功能门的标准过程。

我自己在RFC和现在之间的Const泛型中的参与很少,甚至根本不存在,本质上,我的角色(无论是在RFC还是现在)一直是确定一个我们可以就向前推进达成共识的范围。然而,在此期间,实现const泛型的艰苦工作已经进行了多年,我要特别赞扬贡献者eddyb、varkor、lcnr和oli-obk,他们在实现这一要求很高的特性方面所做的工作。