Rust newbie here that has been struggling for a full day on how to get the compiler to recognize that the lifetime of a lazy_static struct instance is ‘static. A minimal example of what I am trying to do is the following:
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()
}
But I am getting the following error:
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
I don’t understand this because RECEIVER_CONNECTIONS is a lazy_static variable and I don’t think my code uses the receiver_connections_instance
past the end of the function.
Many thanks and infinite karma to whoever can help me understand what I’m doing wrong here. 🙂
2
Answers
The TLDR is that the relevant reference is not
'static
because it’s tied to the lifetime of the mutex guard. I’ll explain the issue by walking through the relevant parts of the code.You start by locking the
RECEIVER_CONNECTIONS
mutex, storing the guard inreceiver_connections_instance
:Then you get a mutable reference to data inside the guard, and store it in
receiver_connection
:You then call the
as_pubsub()
method onreceiver_connection
and store the result inreceiver
:The signature of that
as_pubsub()
method is the following:which if we un-elide the lifetimes can be written as
We can see from the lifetimes that the return type
PubSub
captures the input lifetime. (This is becausePubSub
stores the mutable reference inside itself). So all of this means the lifetime ofreceiver
is bound to the lifetime of the mutex guard. The code that follows then tries to storereceiver
in the staticRECEIVERS
variable, but that cannot work becausereceiver
cannot outlive the mutex guardreceiver_connections_instance
, which is dropped at the end of the function.The problem is that your
Connection
isn’t'static
at the time you invokeas_pubsub()
, you access it through a mutex guard with a limited lifetime. As soon as you drop the guard, the connection is no longer exclusively yours, and neither is thePubSub
– which is whyPubSub<'static>
is not allowed. The redis Rust API doesn’t seem to allow exactly what you’re after (at least without unsafe), becauseConnection::as_pubsub()
requires&mut self
, prohibiting you from invokingas_pubsub()
directly on a globally storedConnection
.But since your connections are global and never removed anyway, you could simply not store the connection, but "leak" it instead and only store the
PubSub
. Here leak is meant in a technical sense of creating a value that is allocated and then never dropped, much like a global variable, not to an uncontrolled memory leak that would indicate a bug. Leaking the connection gives you&'static mut Connection
which you can use to create aPubSub<'static>
, which you can store in a global variable. For example, this compiles:Several tangential notes:
Client
doesn’t need to be wrapped inMutex
becauseget_connection()
takes&self
.unwrap()
is appropriate.Option<u64>
orResult<u64>
to signal that a value could not be returned. That allows the function to use the?
operator.The code above has these improvements applied, resulting in a significantly reduced line count.