rustiron

How does the Iron framework apply a tuple of modifiers to Response::with?


I am looking into the Iron source code for Response::with(), trying to understand how it applies a tuple as modifiers to the response.

As I understand, a modifier is simply a builder object, taking in a reference to the current context (self) and taking the object you wish to build on as a parameter (as long as you implement the modify function).

Assuming we have the following code:

use iron::modifiers::Header;

fn hello_world(_: &mut Request) -> IronResult<Response> {
    let string = get_file_as_string("./public/index.html");
    let content_type = Header(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
    Ok(Response::with((status::Ok, string, content_type)))
}

Digging through the docs, I can see the implementation of Response::with() in Iron is as follows:

pub fn new() -> Response {
    Response {
        status: None, // Start with no response code.
        body: None, // Start with no body.
        headers: Headers::new(),
        extensions: TypeMap::new()
    }
}

/// Construct a Response with the specified modifier pre-applied.
pub fn with<M: Modifier<Response>>(m: M) -> Response {
    Response::new().set(m)
}

I'm struggling to see how my tuple of objects are translated into modifiers? I'd expect to see a foreach potentially iterating over each modifier, but here I simply see a set operation.

Could somebody explain the order to execution here and uncover what is actually happening?


Solution

  • Interesting question! Let's look at the function signature again:

    fn with<M: Modifier<Response>>(m: M) -> Response
    

    This means that with accepts exactly one argument that implements Modifier<Response>. So next we could look up what types do implement the trait Modifier. In the documentation we see that it's not only implemented for String or Status, but for tuple types, too! Those implementations are written in this file. For example, let's look at this impl:

    impl<X, M1, M2, M3> Modifier<X> for (M1, M2, M3)
        where M1: Modifier<X>,
              M2: Modifier<X>,
              M3: Modifier<X> 
    {
        fn modify(self, x: &mut X) {
            self.0.modify(x);
            self.1.modify(x);
            self.2.modify(x);
        }
    }
    

    This implements the trait for every tuple of size 3 which elements also implement Modifier. And the implementation of modify is just to call the modify-implementation of each tuple element; this is the foreach you were searching for.