3

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));
malbarbo
  • 10,717
  • 1
  • 42
  • 57

1 Answers1

4

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.

malbarbo
  • 10,717
  • 1
  • 42
  • 57
  • Many thanks for this. Now I did not state this in my original problem, but I have more than one of these router.get-lines ( which is the point of using a router) and adding a second line like that ( router.get("/hi", move |...| hi(...)); ) will result in new compiler errors. error: capture of moved value: `context` –  Aug 12 '16 at 11:41
  • Again many thanks for this. There is a lot I do not understand here ( why do I need to clone everything, when the content of the struct will be the same, why do I need to 'move' my closure and what does that even mean, why are these derives at my struct ). I read the rust programming book/doc thing and now just realize that I don't really understood what they wrote there. –  Aug 12 '16 at 12:15
  • 1
    You can also use the [persistent](https://github.com/iron/persistent) crate for shared data instead of wrapping it in an Arc and Mutex. – squiguy Aug 12 '16 at 20:42
  • if you guys get this error, change port 80 to 3000: thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Io(Error { repr: Os { code: 13, message: "Permission denied" } })', src/libcore/result.rs:906:4 @malbarbo could you edit to port 3000 so less people get this stupid little error? :) – Felipe Valdes Mar 10 '18 at 02:46