Rust有一个RFC与非词汇生命周期有关,has been approved要在语言中实现很长时间。 Recently,Rust对此功能的支持已经有了很大的改进,并且被认为是完整的。
我的问题是:究竟什么是非词汇生命?
答案 0 :(得分:70)
通过了解lexical有效期是什么,最容易理解非词汇生命周期是什么。在存在非词法生命周期之前的Rust版本中,此代码将失败:
fn main() {
let mut scores = vec![1, 2, 3];
let score = &scores[0];
scores.push(4);
}
Rust编译器看到scores
变量借用score
,因此不允许进一步突变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
从未使用过!问题是scores
score
的借用是lexical - 它会一直持续到包含它的块的末尾:
fn main() {
let mut scores = vec![1, 2, 3]; //
let score = &scores[0]; //
scores.push(4); //
// <-- score stops borrowing here
}
非词汇生命周期通过增强编译器来理解这种详细程度来解决这个问题。编译器现在可以更准确地判断何时需要借用,并且此代码将被编译。
关于非词汇生命周期的一个奇妙的事情是,一旦启用,没有人会想到它们。它将简单地变成&#34; Rust所做的&#34;事情会(希望)正常工作。
Rust旨在仅允许已知安全程序进行编译。但是,it is impossible完全允许仅安全程序并拒绝不安全程序。为此,Rust在保守方面犯了错误:一些安全程序被拒绝。词汇生命周期就是其中的一个例子。
词汇生命周期很多在编译器中更容易实现,因为块的知识是“平凡的”,而数据流的知识则不那么简单。编译器必须是rewritten to introduce and make use of a "mid-level intermediate representation" (MIR)。然后必须重写借用检查器(a.k.a。&#34; borrowck&#34;)以使用MIR而不是抽象语法树(AST)。然后必须将借用检查器的规则细化为更细粒度。
词汇生命期并不总是妨碍程序员,并且有许多方法可以解决词汇生命周期,即使它们很烦人。在许多情况下,这涉及添加额外的花括号或布尔值。这使得Rust 1.0可以在非词汇生命周期实施之前运行多年。
有趣的是,某些好的模式是由于词汇生命周期而开发的。我的主要例子是the entry
pattern。这段代码在非词汇生命周期之前失败并用它编译:
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;
}
值的生命周期是值保持在特定内存地址的时间跨度(有关详细说明,请参阅Why can't I store a value and a reference to that value in the same struct?)。被称为非词汇生命周期的特征并没有改变任何值的生命周期,因此它不能使生命周期成为非词汇。它只会使跟踪和检查这些值的借用更加精确。
该功能的更准确名称可能是&#34;非词汇借用&#34;。一些编译器开发人员参考基础的基于MIR的借用&#34;。
非词汇生命期从来没有打算成为面向用户的&#34;功能,本身。由于我们从他们的缺席中获得了很少的剪切,他们在我们的脑海中大部分都变得很大。他们的名字主要用于内部开发目的,为了营销目的而改变它并不是优先考虑的事情。
在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"
如果你还在使用2015版本,那么稳定的Rust中还没有非词汇生命周期。在每晚版本的Rust中,你可以通过一个功能选择加入NLL:
#![feature(nll)]
您甚至可以使用编译器标记-Z polonius
选择加入NLL的实验版本。