我在正确理解 Rust 中的生命周期概念时遇到了一些麻烦。特别是在涉及结构的对象本身和结构的成员时。
以下最小示例显示了我遇到的一个问题:
use redis::{Connection, PubSub};
pub struct Db<'a> {
conn: Connection,
pubsub: Option<PubSub<'a>>
}
impl<'a> Db<'a> {
fn open(address: &str) -> Self {
let client = redis::Client::open(address).unwrap();
let conn = client.get_connection().unwrap();
Self {conn, pubsub: None}
}
fn subscribe(&'a mut self) {
let pubsub = self.conn.as_pubsub();
self.pubsub = Some(pubsub)
}
}
fn main() {
let mut db = Db::open("localhost");
db.subscribe();
}
Run Code Online (Sandbox Code Playgroud)
编译结束并出现以下错误:
error[E0597]: `db` does not live long enough
--> src/main.rs:22:5
|
22 | db.subscribe();
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
23 | }
| -
| |
| `db` dropped here while still borrowed
| borrow might be used here, when `db` is dropped and runs the destructor for type `Db<'_>`
For more information about this error, try `rustc --explain E0597`.
Run Code Online (Sandbox Code Playgroud)
我需要pubsub变量作为结构的成员,并且PubSub类型有一个引用的字段redis::Connection。因此,必须明确地定义该结构的生命周期。
据我了解,在subscribe函数中我定义对象本身也必须至少存在一个a生命周期。然而,编译错误告诉我,当db被删除时,它仍然是通过pubsub引用借用的。删除不是db意味着删除引用,因为它是结构的成员吗?
我应该怎么做才能修复这个错误?我的目标是仍然具有pubsub利用. 我尝试过为定义另一个生命周期,但没有实现任何目标,并且代码仍然无法编译。subscribeself.connbself
这是析构函数的一个棘手问题,特别是所谓的“Drop Check”。错误消息将此称为
borrow might be used here, when `db` is dropped and runs the destructor for type `Db<'_>`
Run Code Online (Sandbox Code Playgroud)
您可以在此处阅读有关 Drop Check 的所有详细信息,您会发现您基本上复制了本文中给出的示例。
问题始于PubSub有一个析构函数(an impl Drop for PubSub)。您的 类型有一个为其创建的隐式析构函数,它在销毁时Db调用PubSub- 和- 成员的析构函数。然而,当-destructor 执行时,它应该可以访问 的生命周期的事物(因为它是一个,来自,来自),并且可能观察到在析构函数开始工作时已经被销毁的生命周期的事物。这是因为 的析构函数在尝试销毁 之前可能已经销毁了其他成员,并且编译器不能排除-析构函数可以返回到部分销毁的。ConnectionDbPubSub'afn drop(&'a mut self)'aPubSub<'a>Db<'a>'aDb<'a>Db<'a>PubSubPubSubDb
这个推理非常有道理: APubSub只是 的薄包装&mut Connection。如果 this&mut Connection引用Connectionin Db(确实如此!),并且析构函数首先Db销毁Connection然后销毁PubSub,则析构函数通过PubSub访问已经销毁的。编译器选择拒绝程序的安全途径。Connection&mut Connection
上面提到的文章展示了解决这个问题的方法。unsafe您可以为实现自己的析构函数Db<'a>,它需要“承诺”pubsub在销毁之前始终销毁conn。然而,由于PubSub实际上只是围绕 的薄层,因此在需要时从中&mut Connection删除成员并创建它可能是最简单的 - 无论如何,它都是零成本操作。pubsubDb<'a>