liveBook · Manning

Question mark operator

As explained in Idiomatic Rust

The ? operator is a Rust postfix operator that propagates a Result error or an absent Option value by returning early from the enclosing function.

Use `?` when a function can pass a failure to its caller rather than handle it immediately. It replaces repetitive or deeply nested matches: a successful `Result` yields its value, while an `Err` returns early. With `Option`, a present value continues and `None` returns early. The enclosing function must return a compatible `Result` or `Option`. section 3.1.2 section 4.5

Write a file without nested matches

```rust fn write_to_file() -> std::io::Result<()> { use std::fs::File; use std::io::prelude::*; let mut file = File::create("filename")?; file.write_all(b"File contents")?; Ok(()) } ``` If creating the file fails, writing never starts. If writing fails, that error returns instead. Only two successful calls reach `Ok(())`. The caller can still match on the returned result to print a success or failure message. section 3.1.2

When chained calls have different error types, the enclosing function’s error type must accept them. Implement `From` for the conversion: `impl From<std::io::Error> for ErrorWrapper` lets a function returning `Result<(), ErrorWrapper>` use `?` on file operations. Alternatively, `map_err()` changes a `Result` error explicitly. To cross between `Result` and `Option`, consider `ok()`, `err()`, or `ok_or()`; choose deliberately what information the conversion keeps. section 3.1.2

Listing 4.11 Reading the nth line from a file
use std::path::Path;

#[derive(Debug)]
pub enum Error {                #1
    Io(std::io::Error),         #2
    BadLineArgument(usize),     #3
}

impl From<std::io::Error> for Error {          #4
    fn from(error: std::io::Error) -> Self {
        Self::Io(error)
    }
}

fn read_nth_line(path: &Path, n: usize) -> Result<String, Error> {
    use std::fs::File;
    use std::io::{BufRead, BufReader};
    let file = File::open(path)?;          #5

    let mut reader_lines = BufReader::new(file).lines();    #6
    reader_lines
        .nth(n - 1)                                         #7

        .map(|result| result.map_err(|err| err.into()))     #8
        .unwrap_or_else(|| Err(Error::BadLineArgument(n)))  #9
}

Opening the file uses `?` to propagate an I/O error through a custom error type. Selecting the requested line needs separate handling: the line may be absent, or reading it may fail. `?` does not replace those decisions. section 4.5

Prefer an explicit match or other control flow when failure calls for an action here rather than at the caller. `?` is also a safer default than `unwrap()` when failure should propagate instead of causing a panic. Use `expect()` with a useful message when exiting on failure is genuinely the intended behavior. section 4.5 section 10.3

How Idiomatic Rust covers it

How deep it goes
Explains early return, compatible enclosing return types, and error conversion through `From`, stopping short of broader error-type design.
Examples
Uses file creation and writing, a custom-error conversion, and an nth-line reader with missing-line handling.
What it assumes
Assumes familiarity with Rust functions and return types while explaining how `?` interacts with `Result`, `Option`, and `From`.
What you can do after
Apply it to propagate compatible `Result` or `Option` failures while reserving explicit handling for errors requiring local action.
How it connects
Connects `Result`, `Option`, pattern matching, `From`, `map_err()`, and alternatives such as `unwrap()` and `expect()`.

Also explained in

Compare how 4 books explain question mark operator →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Idiomatic Rust. The section links point to the parts of the book it draws on.