Search code examples
rustiron

How can I pass around variables between handlers


I want to have a context-struct that is available in all handlers, but I am not able to get trough the compiler.

For an example, I want something like this

extern crate iron;
extern crate router;

use iron::prelude::*;
use router::Router;
use std::collections::HashMap;

struct Context {
    cache: HashMap<String, String>,
}

fn main() {
    let mut context = Context { cache: HashMap::new(), };
    let mut router = Router::new();

    router.get("/", |request| index(request, context));

    Iron::new(router).http("localhost:80").unwrap();
}


fn index(_: &mut Request, context: Context) -> IronResult<Response> {
    Ok(Response::with((iron::status::Ok, "index")))
}

This won't compile with a lengthy error message

error: type mismatch resolving `for<'r, 'r, 'r> <[closure@src\main.rs:... context:_] as std::ops::FnOnce<(&'r mut iron::Request<'r, 'r>,)>>::Output == std::result::Result<iron::Response, iron::IronError>`:
 expected bound lifetime parameter ,
    found concrete lifetime [E0271]
src\main.rs:...     router.get("/", |request| index(request, context));

Solution

  • The error message is almost incomprehensible here (there is a issue for it!).

    The problem is that the type of the closure is not being inferred. We can help the compiler annotating the type of request:

    extern crate iron;
    extern crate router;
    
    use iron::prelude::*;
    use router::Router;
    use std::collections::HashMap;
    use std::sync::{Arc, Mutex};
    
    #[derive(Clone, Default)]
    struct Context {
        cache: Arc<Mutex<HashMap<String, String>>>,
    }
    
    fn main() {
        let context = Context::default();
        let mut router = Router::new();
    
        let c = context.clone();
        router.get("/", move |request: &mut Request| index(request, &c), "index");
    
        Iron::new(router).http("localhost:8080").unwrap(); // port 80 is privileged
    }
    
    fn index(_: &mut Request, context: &Context) -> IronResult<Response> {
        Ok(Response::with((iron::status::Ok, "index")))
    }
    

    Note that I changed the type of context to &Context (otherwise, the closure would only implements FnOnce) and use move (the closure must have 'static lifetime to implement Handler).

    To make it possible to change cache in index, you will have to change the type to Arc<Mutex<HashMap<String, String>>> or similar.