rust

Make validation in Rust simple


I have input with 2 variables (x and y). I need to check that they are not Null and both of them don't contain "?". I achieved it with my code, but for me it seems not super optimized. Is it possible (for sure it is, but I don't know how) to improve it?

#[derive(Debug)]
pub enum TestError {
    ContainsQuestionMark,
}

pub type TestResult = Result<bool, TestError>;

fn main() {
    let x = Some(String::from("x"));
    let y = Some(String::from("y"));

    if x.is_some() && y.is_some() {
        let x = x.clone().unwrap();
        let y = y.clone().unwrap();
        if validate(&x, &y).is_ok() {
            out(&x, &y);
        }
    } else {
        println!("x and y are Null");
    }
}

fn validate(x: &String, y: &String) -> TestResult {
    if x.contains("?") || y.contains("?") {
        println!("x or y contains ?");
        return Err(TestError::ContainsQuestionMark);
    }
    Ok(true)
}

fn out(x: &String, y: &String) {
    println!("These are x = {x}, y = {y}");
    std::process::exit(0);
}

Solution

  • You can use if-let to both check and unwrap options at the same time.

    if let Some(x) = &x {
        if let Some(y) = &y {
            if validate(x, y).is_ok() {
                out(x, y);
            }
        } else {
            println!("y is None");
        }
    } else {
        println!("x is None");
    }
    

    You could get fancy and match both at the same time by stuffing &x and &y into a tuple.

    if let (Some(x), Some(y)) = (&x, &y) {
        if validate(x, y).is_ok() {
            out(x, y);
        }
    } else {
        println!("x or y is None");
    }
    

    Playground


    By the way, it's better to take function parameters as &str rather than &String. A &str can accept not only String references, but also static strings and string slices that aren't stored in mutable String buffers. There's no ergonomic cost since &String references automatically convert into &str.

    fn validate(x: &str, y: &str) -> TestResult {
        ...
    }
    
    fn out(x: &str, y: &str) {
        ...
    }