Idiom for parameter parsing

When I'm parsing URL parameters or command line parameters or something like that, the pattern is usually "If the string can be parsed as ... then take it, otherwise return a message", for example:

let zoom: u8 = match (request.get_param("zoom").unwrap_or(String::from("10"))).parse() {
  Ok(zoom) => zoom,
  Err(_) => { return Ok(Response::text("Parameter zoom is invalid").with_status_code(400)) }
};

Is this idiomatic or can it be done better or clearer?

Instead of unwrap_or, better make a match expression on the top level that directly evaluates to 10. No need to allocate a string saying 10 and then parse the string.

request.get_param("zoom").map(str::parse).unwrap_or(Ok(10))
3 Likes

I see the point about the String being uselessly made, but I don't see how I can change the code to avoid that.

Doesn't that change the logic? It's important that when zoom=somethinginvalid is given, we don't silently fall back to the default.

It does not. With no zoom, request.get_param("zoom").map(str::parse) will evaluate to None, and calling unwrap_or on it will return the default Ok(10). With an invalid zoom, request.get_param("zoom").map(str::parse) will evaluate to Some(Err(..)), and unwrap_or will just unwrap the Some.

1 Like

Oh, I see, None passes through map() unchanged while a String will be transformed into a Result<u8, ParseIntError>. :bulb:

What I would normally do is use the anyhow crate because it has this useful Context extension trait which lets you call the context() method on any Option or Result to turn it into a result wrapped with that error message.

use anyhow::{Error, Context};

fn handle_request(request: &Request) -> Result<Response, Error> {
  let zoom = request.get_param("zoom")
    .context("Unable to get the \"zoom\" parameter")?;

  ...
}

I am actually using anyhow. The problem here is that I have those "parameter errors" that should generate a 400 Response with a description and I also have actual errors for which I use ? that should generate an empty 500 Response. I don't see how I can use ? for both.

Hmm... Depending on how often you do this, what about introducing your own Context-like extension trait which adds methods like bad_request() and internal_server_error() to Option and Result to convert the error into a response with the correct error code?