Search code examples
rustoption-type

Why is Option<T>::and_then() not mutually exclusive from a following .unwrap_or()?


Why is it that Option::and_then() doesn't process exclusively from a following Option::unwrap_or()? Shouldn't the and_then() only happen if the Option is a Some() and then the .unwrap_or() only happens if the Option is a None? Here's a code example, the first method triggers a complaint from the borrow checker, and the later method does not, but theoretically shouldn't they be doing the same thing?

use std::collections::HashMap;

#[derive(Debug)]
struct Response {
    account: String,
    status: String,
    error: String,
}

fn main() {

    let num = String::from("426238");
    let record = {
        Response {
            account: "".into(),
            status: "failed".into(),
            error: "Invalid Account".into(),
        }
    };
    let mut acct_map = HashMap::new();
    acct_map.insert(&num, record);
    
    // Doesn't work
    let record = acct_map.remove(&num)
    .and_then(|mut r| {r.account = num; Some(r)}) // Should only get processed if there's a Some()
    .unwrap_or( // Should only get processed if there's a None
        Response {
            account: num,
            status: String::from("failed"),
            error: String::from("The server did not return a response for this account."),
        }
    ); // Yet rustc says variable moved from .and_then()
    
    // Works
    let num = String::from("426238");
    let record = if let Some(mut response) = acct_map.remove(&num) {
        response.account = num;
        response
    } else {
        Response {
            account: num,
            status: String::from("failed"),
            error: String::from("The server did not return a response for this account."),
        }
    };
}

After I got that complaint while trying the former, I switched to the later given it is more understandable and actually works, but I'm wondering if there is more behind .and_then() and .unwrap_or() than what my understanding is.


Solution

  • First of, since you used unwrap_or rather than unwrap_or_else, the parameter of unwrap_or will always be executed, which means it will always move out num.

    Second, even if you had used unwrap_or_else, nothing in the signature of and_then or unwrap_or_else tell the borrow checker that these methods are mutually exclusive, therefore in its eyes, both lambdas could execute. This isn't allowed.

    if let is the way to go here.