Rust is built on explicit error management: exceptions are absent, instead, a returned value Result<T, E> is used. This ensures safety and predictability of code.
Background:
Many languages have taken the approach of exceptions, leading to unexpected situations and the need to explicitly handle exceptions at runtime. From the very beginning, Rust focused on type control, where all errors must be part of the function signature.
Problem:
The main task is to write code where errors are not ignored or masked, there are no "panic" functions, but at the same time, the code remains compact and readable. It's necessary to correctly propagate errors upward without losing information about their type, and to avoid complicating the logic.
Solution:
The key tool is the type Result<T, E> and the ? operator, which automatically "unwraps" the result: on error, it causes an immediate exit from the function with the return of the error, and on success, it returns the value.
Example code:
fn read_number(file: &str) -> Result<i32, std::io::Error> { let content = std::fs::read_to_string(file)?; let num: i32 = content.trim().parse()?; Ok(num) }
Key features:
? operator simplifies nesting (removes if-let/unwrap/expect).map_err() and other methods)Can ? be used in functions that return Unit (void)?
No, the ? operator is only allowed within functions that return Result or Option. If a function returns (), ? cannot be used.
What happens if the error types in multiple calls with ? are different?
It results in a compilation error: the error type must be clearly defined. You need to either unify all errors to the same type using .map_err(), or use thiserror, or describe an enum wrapper at the API level. Example:
fn foo() -> Result<_, MyError> { let a = bar()?; let b = baz().map_err(MyError::Baz)?; Ok(…) }
What is dangerous about .unwrap() in the inner logic?
There is a common misconception that in "main" code, unwrap() can be safely used, "it definitely won't panic". In reality, even a small invisible error can lead to a runtime panic — compromising the safety of the program.
unwrap/expect, especially in inner logic?Production code retained many unwrap() after quick debugging. As a result, the application crashed on any parsing failure due to incorrect user input.
Pros:
Cons:
Used the full stack of Result<T, E> with explicit error descriptions and applied only ? and .map_err(), preserving all failure information.
Pros:
Cons: