Skip to content

What unwrap does

Level: 101 · for newcomers

One line: unwrap() hands you the value inside an Ok or a Some, and panics on an Err or a None. The compiler checks nothing about whether that will happen: it is a method that decides at run time.

fn main() {
    let good: f32 = "1.5".parse().unwrap(); // 1.5
    let row = vec![1.0_f32, -1.0, 1.0];
    let last = row.get(2).unwrap(); // &1.0
    println!("{good} {last}"); // 1.5 1
}

The method, in full

On a Result, unwrap is one match. This is its source in core as shipped with rustc 1.98.0, attributes trimmed:

#[track_caller]
pub fn unwrap(self) -> T
where
    E: fmt::Debug,
{
    match self {
        Ok(t) => t,
        Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e),
    }
}

unwrap_failed is a one-line helper that runs panic!("{msg}: {error:?}"). Option::unwrap is the same match with Some(val) => val, and its None arm panics with a fixed sentence. Blocks 1 and 2 of the verified output show both messages:

  • called `Result::unwrap()` on an `Err` value: ParseFloatError { kind: Invalid }
  • called `Option::unwrap()` on a `None` value

It checks nothing at compile time

Explanations sometimes say that unwrap tells the compiler the result will be fine. The compiler is not told anything. This builds with no warning and panics when it runs:

let text = "x";
let n: f32 = text.parse().unwrap(); // compiles; panics at run time
One run — exit status 101
thread 'main' (29385617) panicked at unwrap_x.rs:3:31:
called `Result::unwrap()` on an `Err` value: ParseFloatError { kind: Invalid }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Two details in that message. 3:31 is the unwrap() in your file, not a line inside the standard library: unwrap is marked #[track_caller] (in the source above), so the panic reports where it was called. And the error is printed with {:?}, the Debug form, not the Display sentence invalid float literal (block 4). What a panic costs explains the rest: the exit status 101, and what is unwound.

In a test, the same panic makes the test fail, which is why unwrap in test code is ordinary. A first test, step by step shows a test failing exactly this way.

The error type must implement Debug

Because the message prints the error with {:?}, Result::unwrap only exists when E: Debug:

Abridged — real rustc 1.98.0 output for no_debug.rs, the note pointing into std dropped
error[E0277]: `BadRow` doesn't implement `Debug`
    --> no_debug.rs:8:26
     |
   8 |     let row = read_row().unwrap();
     |                          ^^^^^^ the trait `Debug` is not implemented for `BadRow`
     |
help: consider annotating `BadRow` with `#[derive(Debug)]`
     |
   1 + #[derive(Debug)]
   2 | struct BadRow;
     |

The methods beside it

Block 3 sends the same Err through each:

Method On Ok(v) / Some(v) On Err / None
unwrap() v panics with the error's Debug
expect("…") v panics with your sentence, then the error
unwrap_or(x) v x
unwrap_or_else(f) v f(), called only now
unwrap_or_default() v the type's default: 0.0 for f32
? v returns the Err or None from the function
.ok() Some(v) None

When unwrap is the right call

  • In tests and examples, where a panic is the failure report you want.
  • When the value cannot be missing and you can say why, which is when expect is better, because it writes the reason down.
  • Not on input: text from a user, a file or the network can always be wrong, and a crash is rarely the right answer to that. unwrap is a TODO you forgot to remove is about how unwraps arrive in real code and how to find them.

The verified output

Verified output of what_unwrap_does.rs — regenerated by tools/run_examples.py, never hand-typed.

1. On a Result
   "1.5".parse::<f32>().unwrap() returned 1.5
   "x".parse::<f32>().unwrap()   panicked: called `Result::unwrap()` on an `Err` value: ParseFloatError { kind: Invalid }

2. On an Option
   row.get(2).unwrap()  returned 1.0
   row.get(9).unwrap()  panicked: called `Option::unwrap()` on a `None` value

3. The same Err through the methods beside unwrap
   .expect("a CSV field")   panicked: a CSV field: ParseFloatError { kind: Invalid }
   .unwrap_or(0.0)          returned 0.0
   .unwrap_or_default()     returned 0.0
   .ok()                    returned None

4. The message uses the error's Debug form, not its Display form
   Debug:   ParseFloatError { kind: Invalid }
   Display: invalid float literal

If you are coming from another language

Python. unwrap is what Python does by default: float("x") raises, and the program stops unless someone catches it. Rust inverts the default. The failure is a value you must deal with, and unwrap is the explicit request for Python's behaviour. The Option side has no direct equivalent: d["k"] raising KeyError is unwrap on a lookup, and d.get("k") is the Option itself.

ABAP. Calling a method that raises an exception, with no TRY … CATCH around it, ends in a short dump, and that is the closest match to an unwrap that panics. The difference is what shows up in the code: in Rust the risky call carries a visible .unwrap(), so the places that can crash can be found with a search.

See also

Po polsku

unwrap() wyjmuje wartość z Ok albo Some, a na Err albo None wywołuje panikę. To zwykła metoda — w bibliotece standardowej to jeden match — i decyduje w czasie działania programu. Kompilator niczego nie sprawdza: "x".parse::<f32>().unwrap() kompiluje się bez ostrzeżenia i kończy program z kodem 101. Zdanie, że unwrap „mówi kompilatorowi, że wszystko się uda”, jest więc nieprawdziwe.

Komunikat paniki ma dwie cechy warte zapamiętania. Wskazuje linię z .unwrap() w twoim pliku, a nie w bibliotece standardowej, bo metoda ma atrybut #[track_caller]. I wypisuje błąd w postaci Debug (ParseFloatError { kind: Invalid }), a nie Display (invalid float literal) — dlatego Result::unwrap istnieje tylko wtedy, gdy typ błędu implementuje Debug (inaczej E0277).

Obok unwrap stoi cała rodzina: expect (panika z własnym zdaniem), unwrap_or, unwrap_or_else, unwrap_or_default (wartość zastępcza bez paniki), ? (przekazanie błędu wyżej) i ok() (zamiana na Option). unwrap jest na miejscu w testach i przykładach, gdzie panika jest właściwym raportem, oraz tam, gdzie wartość naprawdę nie może być pusta — choć wtedy lepsze jest expect, które zapisuje powód. Na danych wejściowych od użytkownika, z pliku czy z sieci zwykle nie jest.

Szukaj po polsku: panika w Ruscie · rust unwrap · rust called Result::unwrap() on an Err value · rust called Option::unwrap() on a None value · rust unwrap vs expect · rust track_caller