什么是非词汇生命周期?

Posted

技术标签:

【中文标题】什么是非词汇生命周期?【英文标题】:What are non-lexical lifetimes? 【发布时间】:2018-10-19 11:03:12 【问题描述】:

Rust 有一个RFC 与非词汇生命周期相关,has been approved 将在该语言中长期实现。 Recently,Rust 对该功能的支持提升了很多,算是完整的了。

我的问题是:究竟

【问题讨论】:

【参考方案1】:

通过了解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从未使用过!问题是score借用scoreslexical——它一直持续到包含它的块的末尾:

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

非词法生命周期通过增强编译器以了解此详细程度来解决此问题。编译器现在可以更准确地判断何时需要借用,并且此代码将编译。

非词法生命周期的美妙之处在于,一旦启用,没有人会想到它们。它将简单地成为“Rust 所做的事情”,并且事情会(希望)正常工作。

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

Rust 只允许编译已知安全的程序。但是,it is impossible 完全允许 安全程序并拒绝不安全程序。为此,Rust 在保守方面犯了错误:一些安全程序被拒绝。词汇生命周期就是一个例子。

词法生命周期在编译器中更容易实现,因为块的知识是“微不足道的”,而数据流的知识则不那么重要。编译器必须是rewritten to introduce and make use of a "mid-level intermediate representation" (MIR)。然后必须重写借用检查器(又名“borrowck”)以使用 MIR 而不是抽象语法树(AST)。然后必须细化借用检查器的规则以使其更细。

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

有趣的是,某些 good 模式是由于词汇生命周期而开发的。对我来说最好的例子是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? 以获得更长的解释)。被称为非词法生命周期的特性不会更改任何值的生命周期,因此它不能使生命周期成为非词法的。它只会使对这些值的借用的跟踪和检查更加精确。

该功能的更准确名称可能是“非词法借用”。一些编译器开发人员参考了底层的“基于 MIR 的借用”。

非词法生命周期从未打算成为“面向用户”的功能,本身。由于我们从他们的缺席中得到的小剪纸,它们在我们的脑海中大部分都变大了。他们的名字主要用于内部开发目的,而为了营销目的而更改它从来都不是优先事项。

是的,但是我该如何使用它呢?

在 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 的实验版本。

非词汇生命周期解决的实际问题示例

Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in? Why does HashMap::get_mut() take ownership of the map for the rest of the scope? Cannot borrow as immutable because it is also borrowed as mutable in function arguments How to update-or-insert on a Vec? Is there a way to release a binding before it goes out of scope? Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time When returning the outcome of consuming a StdinLock, why was the borrow to stdin retained? Collaterally moved error when deconstructing a Box of pairs

【讨论】:

我认为值得强调的是,也许与直觉相反,非词汇生命周期不是关于变量的生命周期,而是关于借用的生命周期。或者,换句话说,非词汇生命周期是关于将变量的生命周期与借用的生命周期去相关......除非我错了? (但我认为执行析构函数时 NLL 不会改变) 有趣的是,某些好的模式是由于词汇生命周期而开发的”——那么,我想,NLL 的存在可能会使未来的好模式变得更加困难识别? @eggyal 这当然是可能的。在一组约束内进行设计(即使是任意的!)可以带来新的、有趣的设计。如果没有这些限制,我们可能会依赖我们现有的知识和模式,而永远不会学习或探索以找到新的东西。话虽这么说,大概有人会想“哦,哈希计算了两次,我可以解决这个问题”,然后创建 API,但用户一开始可能更难找到 API。我希望像 clippy 这样的工具可以帮助这些人。 也许更好的命名改进是,sub-lexical生命周期,考虑到它特别缩短了绑定的生命周期估计。此外,如前所述,地址粘性与生命周期没有任何关系,因为附加到向量 (push) 可以强制重新分配并因此更改其地址而不会丢失其绑定的引用。对于这个新手来说,生命周期系统似乎都是关于绑定的:所有者、借款者和观察者(也称为共享)。想想看,Rust 中的观察者模式可能非常简单。

以上是关于什么是非词汇生命周期?的主要内容,如果未能解决你的问题,请参考以下文章

Vue的生命周期

Flutter 没有完整的生命周期?

Flutter 没有完整的生命周期?

Flutter 没有完整的生命周期?

聊聊架构--读书笔记

转spring bean的生命周期