什么是非词汇生命期?

Rust 有一个与非词法生命周期相关的 RFC已经被批准了将在该语言中实现很长时间。Rust 对这个特性的支持已经有了很大的改进,被认为是完整的。

我的问题是: 什么是非词汇生活?

20002 次浏览

通过理解 < em > lexical 生命周期是什么,最容易理解什么是非词汇生命周期。在 Rust 的非词汇生命周期出现之前的版本中,这段代码将会失败:

fn main() {
let mut scores = vec![1, 2, 3];
let score = &scores[0];
scores.push(4);
}

Rust 编译器发现 scoresscore变量借用,因此它不允许 scores的进一步变异:

error[E0502]: cannot borrow `scores` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 |     let score = &scores[0];
|                  ------ immutable borrow occurs here
4 |     scores.push(4);
|     ^^^^^^ mutable borrow occurs here
5 | }
| - immutable borrow ends here

然而,人们可以看到这个例子过于保守: score从没用过!问题在于,scorescores的借入是 < em > lexical ーー这种借入一直持续到包含 scores的区块的末端:

fn main() {
let mut scores = vec![1, 2, 3]; //
let score = &scores[0];         //
scores.push(4);                 //
// <-- score stops borrowing here
}

非词汇生命周期通过增强编译器来理解这个级别的细节来修复这个问题。编译器现在可以更准确地判断什么时候需要借用,这段代码将被编译。

非词汇生命周期的一个奇妙之处在于,一旦启用,没有人会想起他们。它将简单地变成“ Rust 所做的”,事情(希望)将只是工作。

为什么允许词汇生命周期?

Rust 只允许编译已知安全的程序。但是,这是不可能的准确地允许 只有安全程序和拒绝不安全的程序。为此,Rust 在保守方面犯了错误: 一些安全程序被拒绝。词汇生命期就是一个例子。

在编译器中,很多的词法生命周期更容易实现,因为块的知识是“琐碎的”,而数据流的知识则不那么琐碎。编译器必须是 改写为引入和使用“中级中间表示”(MIR)。然后,借入检查器(又名“借入”)必须重写,使用 MIR 代替抽象语法树(AST)。然后,借款检查员的规则必须得到改进,以便更加细化。

词汇生命周期并不总是妨碍程序员,而且有很多方法可以在词汇生命周期出现问题时解决它们,即使它们很烦人。在许多情况下,这包括添加额外的花括号或布尔值。这使得 Rust 1.0可以发布,并且在实现非词法生命周期之前可以使用很多年。

有趣的是,某些 很好模式是由于词汇生命周期而发展起来的。对我来说最好的例子是 entry模式。这段代码在非词汇生命周期之前失败,并使用它进行编译:

fn example(mut map: HashMap<i32, i32>, key: i32) {
match map.get_mut(&key) {
Some(value) => *value += 1,
None => {
map.insert(key, 1);
}
}
}

但是,此代码效率低下,因为它将两次计算密钥的哈希值。创建词法生命周期 因为的解决方案更短、效率更高:

fn example(mut map: HashMap<i32, i32>, key: i32) {
*map.entry(key).or_insert(0) += 1;
}

“非词汇生命”这个名字听起来不太对劲

值的生存期是指该值停留在特定内存地址的时间跨度(请参阅 为什么我不能在同一个结构中存储一个值和一个对该值的引用?以获得更详细的解释)。称为非词汇生命周期的特性不会 改变任何值的生命周期,因此它不能使生命周期成为非词汇的。它只是使得跟踪和检查这些值的借用更加精确。

对于这个特性,一个更准确的名称可能是“非词汇 借来的”。

非词汇生命周期从来没有打算成为一个“面向用户”的特性,本质上。它们在我们的脑海中变得越来越大,因为我们从它们的消失中得到了小小的剪纸。它们的名称主要是为了内部开发目的,为了营销目的而改变名称从来都不是优先考虑的事情。

是啊,但我该怎么用呢?

在 Rust 1.31(发布于2018-12-06)中,你需要在 Cargo.toml 中选择加入 Rust 2018版本:

[package]
name = "foo"
version = "0.0.1"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"

从 Rust 1.36开始,Rust 2015版本还支持非词汇生命周期。

当前非词汇生命周期的实现处于“迁移模式”。如果 NLL 借用检查器通过,编译将继续。如果没有,则调用上一个借用检查器。如果旧的借用检查器允许代码,则会打印一个警告,通知您您的代码可能在 Rust 的未来版本中出现故障,并且应该进行更新。

在 Rust 的夜间版本中,您可以通过一个特性标志选择加入到强制破坏中:

#![feature(nll)]

您甚至可以使用编译器标志 -Z polonius选择 NLL 的实验版本。

非词汇生命周期解决的实际问题样本