Search code examples
rusterror-handlingunwrap

Idiomatic way to use unwrap_or/unwrap_or_else with functional casting


I have a situation where I want to read some headers via reqwest::header::HeaderValue

I am doing so using

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let url = "https://httpbin.org/get";
    let resp = reqwest::Client::new().get(url).send().await?;

    // read response headers
    // default if the header does not exist
    let resp_headers: &str = resp
        .headers()
        .get("content-length")
        .unwrap_or(&reqwest::header::HeaderValue::from_str("100").unwrap())
        .to_str()
        .unwrap_or("100");
    println!("{}", resp_headers);

    Ok(())
}

This doesn't seem idiomatic and won't compile. Looking for how to approach a scenario like this where there are several necessary unwraps to process and cast a type into desired end result.


Solution

  • You could use a named block and let-else statements.

    let resp_headers: &str = 'block: {
        let Some(content_length) = resp.headers().get(reqwest::header::CONTENT_LENGTH) else {
            break 'block "100";
        };
        let Ok(content_length) = content_length.to_str() else {
            break 'block "100";
        };
        content_length
    };
    

    Or if you intend to keep the two unwrap values the same, you could use Option::and_then.

    let resp_headers: &str = resp
        .headers()
        .get(reqwest::header::CONTENT_LENGTH)
        .and_then(|content_length| content_length.to_str().ok())
        .unwrap_or("100");
    

    Or you can put this in a function and use ?.

    fn read_content_length(resp: &reqwest::Response) -> Option<&str> {
        resp.headers()
            .get(reqwest::header::CONTENT_LENGTH)?
            .to_str()
            .ok()
    }
    
    let resp_headers: &str = read_content_length(&resp).unwrap_or("100");
    

    But specifically for the content-length header, there is Response::content_length.

    let resp_headers: u64 = resp.content_length().unwrap_or(100);