Search code examples
rustiron

If a Result returns Err(_), I want the whole function to return a HTTP request error


I'm trying to use the Iron framework to build a simple backend in Rust. This handler is just supposed to return the content of a certain file and I can get it to work properly with unwrap() but I want to try to do proper error handling. This is how I would imagine it would look like:

fn get_content(res: &mut Request) -> IronResult<Response> {
    let mut id = String::new();
    res.body.read_to_string(&mut id).unwrap();

    let file_path_string = &("../content/".to_string() + &id + ".rdt");

    // TODO: Error handling
    match File::open(file_path_string) {
        Ok(f) => {
            let mut s = String::new();
            f.read_to_string(&mut s);
            Ok(Response::with(((status::Ok), s)))
        }
        Err(err) => Err(Response::with(((status::InternalServerError), "File not found")))
    };
}

This throws the error not all control paths return a value [E0269], which is fine. But if I add a response after the match part:

match File::open(file_path_string) {
    Ok(f) => {
        let mut s = String::new();
        f.read_to_string(&mut s);
        Ok(Response::with(((status::Ok), s)))
    }
    Err(err) => Err(Response::with(((status::InternalServerError), "File not found")))
};

Err(Response::with(((status::InternalServerError), "File not found")))

I instead get the error message:

expected `iron::error::IronError`,
    found `iron::response::Response`
(expected struct `iron::error::IronError`,
    found struct `iron::response::Response`) [E0308]
src/main.rs:95        
Err(Response::with(((status::InternalServerError), "File not found")))

I think the problem is the collision between Rust Err and Iron Err? I'm not sure though. And I have not done much web development (or Rust for that matter) in the past so any feedback on the code is also appreciated!

UPDATE: I think this is more "The Rust Way" to do it? But I'm not sure

fn get_content(res: &mut Request) -> IronResult<Response> {
    let mut id = String::new();
    res.body.read_to_string(&mut id).unwrap();

    let file_path_string = &("../content/".to_string() + &id + ".rdt");

    // TODO: Error handling
    let f;
    match File::open(file_path_string) {
        Ok(file) => f = file,
        Err(err) => Err(HttpError::Io(err))
    };
    let mut s = String::new();
    f.read_to_string(&mut s);
    Ok(Response::with(((status::Ok), s)))
}

Having the code inside the error handling seems weird as read_to_string also needs to be taken care of and that would create a nested mess of error handling? However, these matching arms are obviously of incompatible types so it won't work... any suggestions?


Solution

  • An Ok() takes an Response, but an Err() takes an IronError.

    Hence your call Err(...) is not valid when ... is a Response!

    How to correct it? Well the first step is, you must create an IronError to send back. I believe (not familiar with Iron) that Iron will automatically an appropriate error code and that it's not your job to do that. In the documentation we find one key type implementing IronError:

    pub enum HttpError {
        Method,
        Uri(ParseError),
        Version,
        Header,
        TooLarge,
        Status,
        Io(Error),
        Ssl(Box<Error + 'static + Send + Sync>),
        Http2(HttpError),
        Utf8(Utf8Error),
        // some variants omitted
    }
    

    I can't see one which allows for an arbitrary string like "file not found". However, your use case is one of an IO failure, right? So it would make sense to use HttpError::Io with the std::IoError that you got back from File::open():

    match File::open(file_path_string) {
        Ok(f) => {
            let mut s = String::new();
            f.read_to_string(&mut s);
            Ok(Response::with(((status::Ok), s)))
        }
        Err(err) => Err(HttpError::Io(err))
    };
    

    By the way, it also fixes your "TODO: error handling"! How beautiful!

    (Code untested, please feel free to edit if compilation fails)