fn main() {
    // Result<T, E> must be handled
    let _result = this_can_fail(false);

    // match pattern
    match this_can_fail(false) {
        Ok(val) => println!("Success: {}", val),
        Err(err) => println!("Error: {}", err),
    }

    // Conditionals
    if this_can_fail(false).is_ok() {
        ()
    } else {
        ()
    }

    // ?
    let result = multiple_possible_fails();

    // Mapping result values
    // Only if `Ok` variant
    let mapped_result = result.map(|val| val.len());
    println!("{:?}", mapped_result);
    // let mapped_err = result.map_err(|err| err);

    // Rust has idiomatic dynamic error type, into which most errors can be converted
    let _result2 = fail_open_file();
}

fn this_can_fail(success: bool) -> Result<String, String> {
    if success {
        Ok(String::from("Success"))
    } else {
        Err(String::from("Error"))
    }
}

fn multiple_possible_fails() -> Result<String, String> {
    this_can_fail(true)?;
    println!("After first error");
    this_can_fail(false)?;
    println!("After second error");
    Ok(String::from("All done."))
}

fn fail_open_file() -> Result<(), Box<dyn std::error::Error>> {
    let f = std::fs::File::open("broken")?;

    let x = "ABC".parse::<i32>()?;

    Ok(())
}
