一生都在为 lazy_static 的价值而奋斗 "borrowed value does not live long enough"
Lifetime struggles with "borrowed value does not live long enough" for lazy_static value
这里的 Rust 新手已经为如何让编译器识别 lazy_static 结构实例的生命周期是“静态的”而苦苦挣扎了一整天。我正在尝试做的一个最小示例如下:
use redis::{Client, Connection, PubSub};
use std::sync::Mutex;
#[macro_use]
extern crate lazy_static;
lazy_static! {
static ref REDIS_CLIENT: Mutex<Client> =
Mutex::new(Client::open("redis://127.0.0.1/").unwrap());
static ref RECEIVER_CONNECTIONS: Mutex<Vec<Connection>> = Mutex::new(vec![]);
static ref RECEIVERS: Mutex<Vec<PubSub<'static>>> = Mutex::new(vec![]);
}
pub fn create_receiver() -> u64 {
let client_instance = match REDIS_CLIENT.lock() {
Ok(i) => i,
Err(_) => return 0,
};
let connection: Connection = match client_instance.get_connection() {
Ok(conn) => conn,
Err(_) => return 0,
};
let mut receiver_connections_instance = match RECEIVER_CONNECTIONS.lock() {
Ok(i) => i,
Err(_) => return 0,
};
let receiver_connection_index = receiver_connections_instance.len();
receiver_connections_instance.push(connection);
let receiver_connection = &mut receiver_connections_instance[receiver_connection_index];
let receiver = receiver_connection.as_pubsub();
let mut receivers_instance = match RECEIVERS.lock() {
Ok(i) => i,
Err(_) => return 0,
};
receivers_instance.push(receiver);
let receiver_handle = receivers_instance.len();
receiver_handle.try_into().unwrap()
}
但我收到以下错误:
error[E0597]: `receiver_connections_instance` does not live long enough
--> src/lib.rs:33:36
|
33 | let receiver_connection = &mut receiver_connections_instance[receiver_connection_index];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
34 | let receiver = receiver_connection.as_pubsub();
| ------------------------------- argument requires that `receiver_connections_instance` is borrowed for `'static`
...
45 | }
| - `receiver_connections_instance` dropped here while still borrowed
我不明白这一点,因为 RECEIVER_CONNECTIONS 是一个 lazy_static 变量,而且我认为我的代码没有在函数末尾使用 receiver_connections_instance
。
非常感谢和无限的业力,无论谁能帮助我理解我在这里做错了什么。 :)
TLDR 是相关参考不是 'static
,因为它与互斥保护的生命周期相关。我将通过浏览代码的相关部分来解释这个问题。
您首先锁定 RECEIVER_CONNECTIONS
互斥锁,将守卫存储在 receiver_connections_instance
:
let mut receiver_connections_instance = match RECEIVER_CONNECTIONS.lock() {
Ok(i) => i,
Err(_) => return 0,
};
然后你在守卫中得到一个对数据的可变引用,并将它存储在receiver_connection
:
let receiver_connection = &mut receiver_connections_instance[receiver_connection_index];
然后您在 receiver_connection
上调用 as_pubsub()
方法并将结果存储在 receiver
:
let receiver = receiver_connection.as_pubsub();
as_pubsub()
方法的签名如下:
fn as_pubsub(&mut self) -> PubSub<'_>
如果我们取消生命周期可以写成
fn as_pubsub<'a>(&'a mut self) -> PubSub<'a>
我们从生命周期可以看出,return类型PubSub
捕获了输入生命周期。 (这是因为 PubSub
将可变引用存储在自身内部)。所以所有这些都意味着 receiver
的生命周期与互斥锁守卫的生命周期相关联。随后的代码尝试将 receiver
存储在静态 RECEIVERS
变量中,但这不起作用,因为 receiver
不能比互斥保护 receiver_connections_instance
长寿,后者在最后被丢弃功能。
问题是在您调用 as_pubsub()
时您的 Connection
不是 'static
,您通过生命周期有限的互斥保护来访问它。一旦你放下警卫,连接就不再是你独有的,PubSub
也不是——这就是为什么 PubSub<'static>
是不允许的。 redis Rust API 似乎不允许 完全 你所追求的(至少没有不安全),因为 Connection::as_pubsub()
需要 &mut self
,禁止您直接在全局存储的 Connection
.
上调用 as_pubsub()
但是由于您的连接是全局的并且无论如何都不会被删除,您可以简单地不存储连接,而是“泄漏”它并且只存储 PubSub
。这里 leak 在技术意义上是指创建一个分配的值,然后永远不会删除,就像一个全局变量,而不是表示错误的不受控制的内存泄漏。泄漏连接会给你 &'static mut Connection
,你可以用它来创建一个 PubSub<'static>
,你可以将它存储在一个全局变量中。例如,这样编译:
lazy_static! {
static ref REDIS_CLIENT: Client = Client::open("redis://127.0.0.1/").unwrap();
static ref RECEIVERS: Mutex<Vec<PubSub<'static>>> = Default::default();
}
pub fn create_receiver() -> RedisResult<usize> {
let connection = REDIS_CLIENT.get_connection()?;
let connection = Box::leak(Box::new(connection)); // make it immortal
let mut receivers = RECEIVERS.lock().unwrap();
receivers.push(connection.as_pubsub());
Ok(receivers.len() - 1)
}
几个切线注释:
- redis
Client
不需要包裹在Mutex
中因为get_connection()
需要&self
.
- 您不需要对每个互斥锁进行模式匹配 - 仅当持有该锁的线程发生恐慌时,锁定才会失败。在那种情况下,您很可能只想传播恐慌,因此
unwrap()
是合适的。
- 使用 0 作为特殊值不是惯用的 Rust,您可以使用
Option<u64>
或 Result<u64>
来表示无法返回值。这允许函数使用 ?
运算符。
上面的代码应用了这些改进,从而显着减少了行数。
这里的 Rust 新手已经为如何让编译器识别 lazy_static 结构实例的生命周期是“静态的”而苦苦挣扎了一整天。我正在尝试做的一个最小示例如下:
use redis::{Client, Connection, PubSub};
use std::sync::Mutex;
#[macro_use]
extern crate lazy_static;
lazy_static! {
static ref REDIS_CLIENT: Mutex<Client> =
Mutex::new(Client::open("redis://127.0.0.1/").unwrap());
static ref RECEIVER_CONNECTIONS: Mutex<Vec<Connection>> = Mutex::new(vec![]);
static ref RECEIVERS: Mutex<Vec<PubSub<'static>>> = Mutex::new(vec![]);
}
pub fn create_receiver() -> u64 {
let client_instance = match REDIS_CLIENT.lock() {
Ok(i) => i,
Err(_) => return 0,
};
let connection: Connection = match client_instance.get_connection() {
Ok(conn) => conn,
Err(_) => return 0,
};
let mut receiver_connections_instance = match RECEIVER_CONNECTIONS.lock() {
Ok(i) => i,
Err(_) => return 0,
};
let receiver_connection_index = receiver_connections_instance.len();
receiver_connections_instance.push(connection);
let receiver_connection = &mut receiver_connections_instance[receiver_connection_index];
let receiver = receiver_connection.as_pubsub();
let mut receivers_instance = match RECEIVERS.lock() {
Ok(i) => i,
Err(_) => return 0,
};
receivers_instance.push(receiver);
let receiver_handle = receivers_instance.len();
receiver_handle.try_into().unwrap()
}
但我收到以下错误:
error[E0597]: `receiver_connections_instance` does not live long enough
--> src/lib.rs:33:36
|
33 | let receiver_connection = &mut receiver_connections_instance[receiver_connection_index];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
34 | let receiver = receiver_connection.as_pubsub();
| ------------------------------- argument requires that `receiver_connections_instance` is borrowed for `'static`
...
45 | }
| - `receiver_connections_instance` dropped here while still borrowed
我不明白这一点,因为 RECEIVER_CONNECTIONS 是一个 lazy_static 变量,而且我认为我的代码没有在函数末尾使用 receiver_connections_instance
。
非常感谢和无限的业力,无论谁能帮助我理解我在这里做错了什么。 :)
TLDR 是相关参考不是 'static
,因为它与互斥保护的生命周期相关。我将通过浏览代码的相关部分来解释这个问题。
您首先锁定 RECEIVER_CONNECTIONS
互斥锁,将守卫存储在 receiver_connections_instance
:
let mut receiver_connections_instance = match RECEIVER_CONNECTIONS.lock() {
Ok(i) => i,
Err(_) => return 0,
};
然后你在守卫中得到一个对数据的可变引用,并将它存储在receiver_connection
:
let receiver_connection = &mut receiver_connections_instance[receiver_connection_index];
然后您在 receiver_connection
上调用 as_pubsub()
方法并将结果存储在 receiver
:
let receiver = receiver_connection.as_pubsub();
as_pubsub()
方法的签名如下:
fn as_pubsub(&mut self) -> PubSub<'_>
如果我们取消生命周期可以写成
fn as_pubsub<'a>(&'a mut self) -> PubSub<'a>
我们从生命周期可以看出,return类型PubSub
捕获了输入生命周期。 (这是因为 PubSub
将可变引用存储在自身内部)。所以所有这些都意味着 receiver
的生命周期与互斥锁守卫的生命周期相关联。随后的代码尝试将 receiver
存储在静态 RECEIVERS
变量中,但这不起作用,因为 receiver
不能比互斥保护 receiver_connections_instance
长寿,后者在最后被丢弃功能。
问题是在您调用 as_pubsub()
时您的 Connection
不是 'static
,您通过生命周期有限的互斥保护来访问它。一旦你放下警卫,连接就不再是你独有的,PubSub
也不是——这就是为什么 PubSub<'static>
是不允许的。 redis Rust API 似乎不允许 完全 你所追求的(至少没有不安全),因为 Connection::as_pubsub()
需要 &mut self
,禁止您直接在全局存储的 Connection
.
as_pubsub()
但是由于您的连接是全局的并且无论如何都不会被删除,您可以简单地不存储连接,而是“泄漏”它并且只存储 PubSub
。这里 leak 在技术意义上是指创建一个分配的值,然后永远不会删除,就像一个全局变量,而不是表示错误的不受控制的内存泄漏。泄漏连接会给你 &'static mut Connection
,你可以用它来创建一个 PubSub<'static>
,你可以将它存储在一个全局变量中。例如,这样编译:
lazy_static! {
static ref REDIS_CLIENT: Client = Client::open("redis://127.0.0.1/").unwrap();
static ref RECEIVERS: Mutex<Vec<PubSub<'static>>> = Default::default();
}
pub fn create_receiver() -> RedisResult<usize> {
let connection = REDIS_CLIENT.get_connection()?;
let connection = Box::leak(Box::new(connection)); // make it immortal
let mut receivers = RECEIVERS.lock().unwrap();
receivers.push(connection.as_pubsub());
Ok(receivers.len() - 1)
}
几个切线注释:
- redis
Client
不需要包裹在Mutex
中因为get_connection()
需要&self
. - 您不需要对每个互斥锁进行模式匹配 - 仅当持有该锁的线程发生恐慌时,锁定才会失败。在那种情况下,您很可能只想传播恐慌,因此
unwrap()
是合适的。 - 使用 0 作为特殊值不是惯用的 Rust,您可以使用
Option<u64>
或Result<u64>
来表示无法返回值。这允许函数使用?
运算符。
上面的代码应用了这些改进,从而显着减少了行数。