我读过What are non-lexical lifetimes?。使用非词法借用检查器,可以编译以下代码:
fn main() {
let mut scores = vec![1, 2, 3];
let score = &scores[0]; // borrows `scores`, but never used
// its lifetime can end here
scores.push(4); // borrows `scores` mutably, and succeeds
}
在上述情况下,这似乎是合理的,但是当涉及到互斥锁时,我们不希望过早释放它。
在下面的代码中,我想首先锁定共享结构,然后执行关闭,主要是为了避免死锁。但是,我不确定该锁是否会过早释放。
use lazy_static::lazy_static; // 1.3.0
use std::sync::Mutex;
struct Something;
lazy_static! {
static ref SHARED: Mutex<Something> = Mutex::new(Something);
}
pub fn lock_and_execute(f: Box<Fn()>) {
let _locked = SHARED.lock(); // `_locked` is never used.
// does its lifetime end here?
f();
}
Rust是否会特别对待锁,以确保其使用生命周期可以延长到范围的尽头?我们是否必须像下面的代码那样显式地使用该变量,以避免过早丢失锁?
pub fn lock_and_execute(f: Box<Fn()>) {
let locked = SHARED.lock(); // - lifetime begins
f(); // |
drop(locked); // - lifetime ends
}
最佳答案
这里有一个误解:NLL(非词法生存期)影响借位检查,而不影响对象的实际生存期。
Rust广泛使用RAII1,因此许多对象(例如锁)的Drop
实现都有副作用,这些副作用必须在执行流程中确定且可预测的点上发生。
NLL不会更改此类对象的生存期,因此它们的析构函数在与之前完全相同的时间执行:在其词法作用域的末尾,以相反的创建顺序执行。
NLL确实改变了对使用期限以进行借阅检查的编译器的理解。实际上,这不会导致任何代码更改。这纯粹是分析。进行此分析更加巧妙,以更好地识别使用引用的实际范围:
如果是
Ref<'a>
(来自RefCell
),则Ref<'a>
将在词法作用域的末尾删除,此时它将使用对RefCell
的引用来减少计数器。NLL不会剥离抽象层,因此必须考虑到任何包含引用的对象(例如
Ref<'a>
)都可以在其Drop
实现中访问该引用。结果,任何包含引用的对象(例如锁)都将强制NLL认为引用的“使用中”范围会一直扩展到被丢弃为止。1资源获取是初始化,其原始含义是,一旦执行了变量构造函数,它便获取了所需的资源,并且未处于半熟状态,这通常用来表示该变量的破坏将被释放。它拥有的任何资源。
关于rust - 非词汇生命周期借用检查器是否会过早释放锁定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63122567/