Arc<RwLock<>> doesn't live long enough


#1

I’m writing a function as follows. req.get::<State>().unwrap() gets me a Arc<RwLock. When I try to do a read access on that and access functions in it, I get these errors -

service_reader does not live long enough
service_state does not live long enough

pub fn collect_stats( &self, req: &mut Request ) -> Vec<ServiceStatus> {

            let mut statuses = Vec::new();
          
            for service in self.services.iter() {

                match *service {
                    ServiceEnum::RM => {
                         let service_state = req.get::<State<SomeService>>().unwrap();
            let service_reader = service_state.read().unwrap();
            println!("{}", service_reader.service_name());
                        statuses.push(ServiceStatus{ name: service_reader.service_name(), data: service_reader.data()});
                    },
                    _=> doNothing(),
                }

            }

            return statuses;
        }

#2

Can you please post the entire error message? Also, what’s the full return type of req.get()? And what’s the definition of ServiceStatus?


#3

My guess is service_reader.service_name() and/or service_reader.data() is returning a reference tied to service_reader. That data is only accessible while the rwlock is held, which is limited to the body of that match arm. However, the code is attempting to add this to a Vec that outlives this block.

But as @jethrogb said, we need more info to say for sure.


#4

Full error msg:
error[E0597]: service_state does not live long enough
–> src/service/mod.rs:139:30
|
139 | let service_reader = service_state.read().unwrap();
| ^^^^^^^^^^^^^ does not live long enough

161 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 131:5…
–> src/service/mod.rs:131:5
|
131 | / pub fn collect_stats( &self, req: &mut Request ) -> Vec {
132 | |
133 | | let mut statuses = Vec::new();
134 | | /* let service_state = req.get::<State>().unwrap();
… |
160 | | return statuses;
161 | | }
| |_____^

‘req’ is an iron request. ServiceStatus is just a struct of ‘name’ and ‘data’ fields


#5

I guess you are right, so how do I add to a Vec inside the match arm and use that outside ?


#6

Can you paste the definition of ServiceStatus and of the service_name() and data() methods?


#7

@vitalyd

#[derive(Serialize)]
pub struct ServiceStatus<'a> {
service_name: &'static str,
service_data: &'a Value
}

impl Status for SomeService {
fn service_name(&self) ->
&'static str { “SomeService” }

fn service_data(&self) -> &Value {
    &self.config
}

}

pub trait Status : Send + Sync {
fn service_name(&self) -> &'static str;
fn service_data(&self) -> &Value;
}


#8

This is the problem:

Can you clone the Value? Is that (relatively) cheap? That would be the simplest fix: clone the value and store it in ServiceStatus, which in turn is added to the Vec that you return.


#9

I tried cloning and adding a reference. I still get the error -

borrowed value does not live long enough
–> src/service/mod.rs:142:150
|
142 | statuses.push(ServiceStatus{ service_name: service_reader.service_name(), is_healthy: service_reader.is_healthy(), service_data: &service_reader.service_data().clone()});
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| does not live long enough
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 131:5…


#10

ServiceStatus would own a Value itself, not have a reference (which is what you’re trying to do above, to a temporary nonetheless). So it would look like:

// no more lifetime parameter 'a
struct ServiceStatus {   
    service_name: &'static str,
    is_healthy: bool, // ??
    service_data: Value, <== holding a value here, not a reference
}

statuses.push(ServiceStatus { 
     service_name: service_reader.service_name(), 
     is_healthy: service_reader.is_healthy(), 
     service_data: service_reader.service_data().clone(),
   });

I’m going to assume is_healthy is just a bool or some other Copy type.


#11

Got it. That solves the lifetime problem. Thank you!