Fred Hors
Fred Hors

Reputation: 4116

How to return Error from `and_then` lambda?

Is there a way to make this code work?

fn set_email(&mut self, email: Option<&str>) -> Result<(), String> {
    self.email = email.and_then(|email| {
        let email = sanitate(email);

        if email.is_empty() {
            if self.needs_email {
                return Err(String::from("the email is needed"));
            }

            return None;
        }

        Some(email)
    });

    Ok(())
}

As you can see I would like to return an Error if the email is needed and is empty. At the same time I'm in assignment and_then lambda. How to do?

Upvotes: 0

Views: 108

Answers (2)

Enselic
Enselic

Reputation: 4902

There is no way to short-circuit the function and return an Err from the top-level function from within the closure. You need to re-structure your code.

If you make sanitate() return None if is_empty() then you can write it like this, which I find nice and simple:

struct ContainsEmail {
    email: Option<String>,
    needs_email: bool,
}

impl ContainsEmail {
    fn set_email(&mut self, email: Option<&str>) -> Result<(), String> {
        self.email = email.and_then(sanitate);
        if self.email.is_none() && self.needs_email {
            Err(String::from("the email is needed"))
        } else {
            Ok(())
        }
    }
}

fn sanitate(email: &str) -> Option<String> {
    let sanitized_email = email.trim().to_lowercase(); // TODO: Do something more useful
    if sanitized_email.is_empty() {
        None
    } else {
        Some(sanitized_email)
    }
}

Upvotes: 1

susitsm
susitsm

Reputation: 485

You can use Option::transpose and Some(Ok(..))

struct Email {
    email: Option<String>,
    needs_email: bool,
}

fn sanitate(s: &str) -> String {
    todo!()
}

impl Email {
    fn set_email(&mut self, email: Option<&str>) -> Result<(), String> {
        self.email = email.and_then(|email| {
            let email = sanitate(email);
    
            if email.is_empty() {
                if self.needs_email {
                    return Some(Err(String::from("the email is needed")));
                }
    
                return None;
            }
    
            Some(Ok(email))
        }).transpose()?;
    
        Ok(())
    }
}

Upvotes: 1

Related Questions