Result. This is an enum type in Rust that indicates whether a function call succeeded or failed. It can return a helpful error message, along with a value.
Some important points. Results must be somehow used—they cannot just be ignored. The question mark operator will return a result from another function call.
Question mark example. This program implements simple result-handling and uses the question mark to propagate an error from another function. In main we call the test() function twice.
And Test() returns a result. The value returned is a usize, and the second argument is an error message string.
Important Test calls test_inner() with a question mark. If test_inner returns Err, test() propagates this result and test does not finish.
fn test_inner(argument: usize) -> Result<usize, &'static str> {
if argument == 6 {
Err("Not valid")
} else {
Ok(argument)
}
}
fn test(argument: usize) -> Result<usize, &'static str> {
// Use question mark to propagate the error from another call.
test_inner(argument)?;
// Valid.
Ok(100)
}
fn main() {
// Call with argument 5, this is valid.
if let Ok(result) = test(5) {
println!("Result: {result}");
}
// Call with argument 6, this causes an error.
let result = test(6).unwrap();
println!("Result: {result}");
}Result: 100
thread main panicked at called Result::unwrap() on an Err value: "Not valid", ...
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
Flatten result iter. It is possible to use flatten() on an iterator of Results. Such iterators are returned by BufReader and its lines() functions.
Version 1 This version of the code uses open() and BufReader to get an iterator of Results, and it uses flatten on them.
Version 2 Here we do the same thing as version A, but we use map() an internal unwrap call.
Info Using flatten() with a Result iterator can be elegant and clear to read. The test1() function is shorter and simpler than test2.
use std::io::*;
use std::fs::*;
pub fn test1(path: &str) -> Vec<String> {
if let Ok(file) = File::open(path) {
let reader = BufReader::new(file);
// Version A: We can use flatten on an iterator of options.
reader.lines().flatten().collect()
} else {
vec![]
}
}
pub fn test2(path: &str) -> Vec<String> {
if let Ok(file) = File::open(path) {
let reader = BufReader::new(file);
// Version B: We can use map to unwrap each option.
reader.lines().map(|l| l.unwrap()).collect()
} else {
vec![]
}
}
fn main() {
println!("{:?}", test1("/Users/sam/test.txt"));
println!("{:?}", test2("/Users/sam/test.txt"));
}Hello
Website
Visitor["Hello", "Website", "Visitor"]
["Hello", "Website", "Visitor"]
Is_ok, is_err. Suppose we call a function that returns a result. We need to handle this result to avoid a compile-time warning.
So We can use the is_ok and is_err functions on the Result that is returned.
Tip These functions are usually used in if-statements—here we print messages if we have an "ok" or "err" result.
use std::fs;
fn main() {
// Try to delete this file, and use is_ok and is_err.
let path = "/does/not/exist";
if fs::remove_file(&path).is_ok() {
println!("Not reached");
}
if fs::remove_file(&path).is_err() {
println!("IS ERR");
}
}IS ERR
IO Result. Usually Results are used with code that handles input and output (file handling). But the concept is the same with any code that returns Result.
A summary. Rust has powerful data types that represent Options and Results. With a Result, we can use a question mark to reuse the result from a function call and return.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Feb 24, 2023 (edit).