Chris Pugmire
Chris Pugmire

Reputation: 91

rust lifetime question, why can't I move a structure '1 layer' up, how do I set the lifetime correctly?

I have a question for you, regarding my 'idx' structure and it's lifetimes... This structure is a cloneable multi threaded object.

This code works, how is it that moving 'idx' one layer up suddenly meant it didn't satify the 'static' lifetime requirement.

main() {
    let w = webtask::Webtask::new();
    // Wait for threads to finish
}

pub fn new() -> Self {
    let (tx, rx) = oneshot::channel();
    let mut idx = fastauth::createindex();
    idx.load_index("users.dat.idx");    
    let lookup= warp::path!("lookup" / String).map(move |name| { Webtask::do_lookup(idx.clone(),name) });
    let routes = lookup; //hello.or(lookup).or(check); 
    let (_addr, server) = warp::serve(routes).bind_with_graceful_shutdown(([127, 0, 0, 1], 3030), async {
         rx.await.ok();
    });  
    let thread = tokio::task::spawn(server);
    Webtask {thread,tx}
}

-------- However, when I try and move 'idx' to the outer layer function I get errors..

main() {
        let mut idx  = fastauth::createindex();
        idx.load_index("users.dat.idx");
        let w = webtask::Webtask::new(&idx);
        // Here I plan to reuse 'idx' as it's really a global structure that will exist the entire time the program is running...
        do_something_else(&idx);
        // then wait for threads to finish...
        exit_when_done();
    }
...
    pub fn new<'inf>(idx: &'inf fastauth::Idx) -> Self {
        let (tx, rx) = oneshot::channel();
        let lookup= warp::path!("lookup" / String).map(move |name| { Webtask::do_lookup(idx.clone(),name) });   // IS CAPTURED HERE 
        let routes = lookup; //hello.or(lookup).or(check); 
        let (_addr, server) = warp::serve(routes).bind_with_graceful_shutdown(([127, 0, 0, 1], 3030), async {  // IS REQUIRED TO LIVE AS LONG AS STATIC HERE. 
             rx.await.ok();
        });   
        let thread = tokio::task::spawn(server);
        // return our 'w' structure...
        Webtask {thread,tx}
    }

idx has lifetime 'inf but it needs to satisfy a 'static lifetime requirement

Upvotes: 0

Views: 135

Answers (1)

Jmb
Jmb

Reputation: 23463

The important lines in the working code are:

let mut idx = fastauth::createindex();
let lookup= warp::path!("lookup" / String).map(
    move |name| { Webtask::do_lookup(idx.clone(),name) });

Here, idx has type fastauth::Idx and is moved inside the closure, so the compiler can guarantee that idx will not be destroyed before the closure.

In the non-working code:

pub fn new<'inf>(idx: &'inf fastauth::Idx) -> Self {
    let lookup= warp::path!("lookup" / String).map(
        move |name| { Webtask::do_lookup(idx.clone(),name) });

Here, idx has type &fastauth::Idx so it is only a reference. That reference is moved inside the closure, but the real Idx is still outside the closure (in the main function), so the compiler can't guarantee that it won't get destroyed before the closure.

Depending on the reason why you wanted to move idx up, you have several solutions:

new can take idx by value instead of taking it by reference

pub fn new(idx: fastauth::Idx) -> Self {

but that means you won't be able to re-use idx in main after you call new, so not really a solution for you.

You can use reference-counting with Rc or Arc

use std::sync::Arc;
main() {
    let mut idx = Arc::new (fastauth::createindex());
    idx.load_index("users.dat.idx");
    let w = webtask::Webtask::new(idx.clone());
    // Here I plan to reuse 'idx' as it's really a global structure that will exist the entire time the program is running...
    do_something_else(idx);
    // then wait for threads to finish...
    exit_when_done();
}

pub fn new(idx: Arc::<fastauth::Idx>) -> Self {
    let (tx, rx) = oneshot::channel();
    let lookup= warp::path!("lookup" / String).map(move |name| { Webtask::do_lookup(idx.clone(),name) });   // IS CAPTURED HERE 
    let routes = lookup; //hello.or(lookup).or(check); 
    let (_addr, server) = warp::serve(routes)
        .bind_with_graceful_shutdown(
            ([127, 0, 0, 1], 3030),
            async {  // IS REQUIRED TO LIVE AS LONG AS STATIC HERE. 
               rx.await.ok();
            });   
    let thread = tokio::task::spawn(server);
    // return our 'w' structure...
    Webtask {thread,tx}
}

Rc and Arc allow multiple contexts to share ownership of a value, so that the value only gets destroyed when the last reference goes out of scope.

You can use lazy_static to create a global immutable static value

use lazy_static;

lazy_static!{
    static ref IDX: fastauth::Idx = {
        let mut idx = fastauth::createindex();
        idx.load_index("users.dat.idx");
        idx
    };
}

See the lazy_static crate for details.

Upvotes: 3

Related Questions