Rust使用lazy_static时出现borrowed value does not live long enough错误如何解决
问题原因
你的核心误区是对静态变量加锁后得到的守卫的生命周期理解有误:
lazy_static声明的RECEIVER_CONNECTIONS本身确实是'static生命周期的静态变量,但你调用.lock()得到的receiver_connections_instance是MutexGuard<Vec<Connection>>类型,它是函数局部变量,函数执行结束就会被销毁,同时自动释放Mutex锁。- 你从守卫中借用的
receiver_connection的生命周期只能和这个守卫绑定,不可能延长到'static,否则就会出现「守卫销毁、锁被释放后,依然持有指向Vec内部元素的引用」的悬垂指针问题,这就是编译器报错的根本原因。 - 你声明的
RECEIVERS要求存储PubSub<'static>类型,而redis的PubSub<'a>的生命周期完全绑定它所借用的Connection的生命周期,你拿不到'static的Connection引用,自然无法生成符合要求的PubSub实例。
解决方案
推荐方案:不全局存储PubSub实例
绝大多数场景下不需要长期持有PubSub实例,你可以仅全局存储Connection列表,需要使用PubSub时临时从Connection生成,使用结束后自动释放守卫和锁,完全规避生命周期问题:
use redis::{Client, Connection, PubSub, RedisError}; 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![]); } 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 idx = receiver_connections_instance.len(); receiver_connections_instance.push(connection); idx.try_into().unwrap() } // 需要订阅/消费消息时,临时生成PubSub使用 pub fn subscribe(receiver_handle: u64, channel: &str) -> Result<(), RedisError> { let mut connections = RECEIVER_CONNECTIONS.lock()?; let conn = connections.get_mut(receiver_handle as usize) .ok_or(RedisError::from((redis::ErrorKind::ClientError, "无效的接收器句柄")))?; let mut pubsub = conn.as_pubsub(); pubsub.subscribe(channel)?; // 在这里处理消息消费逻辑,处理完成后pubsub和锁守卫会自动销毁 Ok(()) }
可选方案:泄漏Connection获取'static引用
如果你确实需要长期持有PubSub实例(比如后台异步跑订阅任务),可以用Box::leak把Connection泄漏为'static生命周期的引用,代价是这些Connection直到程序退出前都不会被释放,对于长期运行的服务通常可以接受:
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 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, }; // 把Connection装箱后泄漏,获得'static生命周期的可变引用 let static_conn = Box::leak(Box::new(connection)); let receiver = static_conn.as_pubsub(); let mut receivers_instance = match RECEIVERS.lock() { Ok(i) => i, Err(_) => return 0, }; receivers_instance.push(receiver); receivers_instance.len().try_into().unwrap() }
内容的提问来源于stack exchange,提问作者outside2344
相关产品推荐
相关产品推荐

