我最不喜欢的铁锈类型

2020-09-22 03:00:32

Struct range<;idx>;{/范围下限(含)。Pub start:idx,/范围上限(独占)。酒吧结束:IDX,}。

IDX通常是一个整数类型,但是您可以创建任何类型的范围,这将在稍后变得很重要。以下是单位范围:

射程有什么问题吗?这将是对拉斯特自己的批评。

任何Rust教程都将涉及借款、使用期限、所有权等。您认为您理解,然后是:

嘿,不,这是因为,如果你想做一款系列Vec>;而不是:

Range<;Vec&>需要借钱,因此更为常见的Range<;usize&>等也迫使它这样做。

我们还没挑完借阅支票呢。Range使您即使在没有所有权转移的情况下也可以进行克隆:

设v=vec![1,2,3,4];设r=1..。2;&;v[r.clone()];//需要避免在下一行&;v[r]中使用";移动值";

范围可以是复制的,但有些范围也是迭代器,您可能会错误地复制一个范围:

设mut iter=0..。N;for I in ITER{if I>;2{Break;}}//ITER ITER.Collect()的静默副本。

因此,没有用作迭代器的Range(许多不能用作迭代器,如Range<;f64>;或Range<;char>;)要付出代价,嵌入范围的任何类型也是如此。

这是在滥用借入检查器作为一个坏的临时针。Range破坏了生命周期和所有权的故事,让借入检查员感觉很武断。

这个倒档范围有效吗?它的len()是0,它不包含(),作为迭代器不会产生任何结果。但是,如果您试图使用它来索引到一个切片中,就会出现恐慌!所以它看起来是有效的,但是已经做好了爆炸的准备!

这是因为-再说一次-你可以做任何东西的范围。如果您试图强制执行start<;=end,您将失去制造一系列不可比较的东西的能力。Dyn错误的范围不能无效,因此int的范围永远不会无效。

一个实际问题是编写正确的边界检查。例如,考虑Slice上的GET_UNCHECKED函数-它说“越界索引是未定义的行为”,但从未定义过越界是什么意思。那么,如何才能安全地调用此函数呢?

重申:起始为0且长度为0的范围可能超出界限。这太疯狂了。

正则表达式引擎(这里是一个)通常处理字符范围,例如/[a-z]/。它是否应该使用Range<;char>;?

不!步枪是/[\u10FFFF]/,这是最大的字符。范围<;字符>;不能表示此值,即使它有这样做的位。

RangeInclusive是更好的选择吗?这使用一个额外的布尔字段来表示…。东西,需要在几个地方分开检查。这是一个愚蠢而昂贵的表示,将RangeInclusive<;char>;推到12个字节,即使它可以容纳8个字节,还有多余的位。对于性能敏感的正则表达式算法来说,这不是一个很好的选择。

问题是射程超载了:它太灵活了,它想要成为一切。

也许为时已晚,我们必须忍受这个疣,但这是一种不同方法的食谱:

将范围限制为复制+PartialEq类型。Range<;String>;或Range<;BigNum>;可能偶尔会用到,但通常情况下不值得强行借用。

既然该范围总是知道如何比较其结束,那么在构造时强制开始<;=end。对于由常量构造的范围,这将是免费的。这避免了“看起来有效,实际上爆炸”的问题,并将解锁进一步的优化:

与其他集合一样,为Range提供一个ITER()方法。现在Range不是迭代器,就像VEC不是迭代器一样,Range可以很容易地复制。这确实意味着在(1..10).iter()中写入n,但是Rust已经要求集合这样做,所以更加一致。

现在Range不是迭代器,RangeInclusive可以删除它的额外布尔值。它只是一对索引,不能为空(这就是SWIFT所做的)。