We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Errors Are Values step 7 of 24
Result, and why it is #[must_use]
Evaluate a list of division ops, each written "a/b", and return one string
per op.
pub fn checked_div(a: i32, b: i32) -> Result<i32, String>
pub fn apply(ops: Vec<String>) -> Vec<String>
-
a well-formed op that divides cleanly → the quotient, e.g.
"10/2"→"5" -
a zero divisor →
"error: division by zero" -
a slash with a non-number on either side →
"error: not a number" -
no slash at all →
"error: malformed"
A failing op must not stop the ones after it, and both sides are trimmed, so
" 12 / 4 " is "3".
The starter compiles. Then the gate deletes it.
Two things are wrong with it, and they are the two things this problem exists to teach.
1. It ignores a Result
warning: unused `Result` that must be used
|
| checked_div(a, b);
| ^^^^^^^^^^^^^^^^^
|
= note: this `Result` may be an `Err` variant, which should be handled
help: use `let _ = ...` to ignore the resulting value
Result is declared #[must_use] in the standard library, so throwing one
away is a warning. And the harness compiles with -D warnings, which
promotes every warning to an error. unused_must_use lives in rustc’s
unused group, so under this gate it is fatal. Your submission does not
merely look sloppy — it does not build.
If you come from a language where ignoring a return value is free, this is the
adjustment. In Java a checked exception forces the caller to write a catch
that most people fill with a comment. In Go the compiler is happy to let err
go unread. In Rust the fallibility is in the type, and a type you never
consume is a value you dropped on the floor — so the same machinery that
notices an unused Vec notices an unused Result.
There is a legitimate opt-out, and it is deliberately visible:
let _ = checked_div(a, b); // "yes, I really mean to ignore this"
r.ok(); is not an opt-out — it produces an Option you also ignore, and
clippy’s unused_result_ok will say so. Being explicit is the point: a
reviewer can grep for let _ =.
2. Its error type is ()
error: this returns a `Result<_, ()>`
|
| pub fn checked_div(a: i32, b: i32) -> Result<i32, ()> {
| ^^^^^^^^^^^^^^^
= help: use a custom `Error` type instead
clippy::result_unit_err is on by default for public functions, and its point
is a design one, not a style one. Result<T, ()> says “this can fail, and I
will tell you nothing about why”. That is exactly the information content of
Option<T> — which is shorter, better supported, and does not mislead the
reader into thinking an error is coming.
If the error carries no information, you wanted
Option. If it does, give it a type.
Here, the error does carry information, so String will do for now. (A String
error is scaffolding — Track 6 replaces it with a real error type later. Do
not conclude that stringly-typed errors are idiomatic Rust.)
What Result actually is
Nothing magical — an enum, exactly like Option, with a second payload:
#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
pub enum Result<T, E> {
Ok(T),
Err(E),
}
There is no unwinding, no stack of handlers, no special control flow. Ok and
Err are ordinary variants; match, if let and let-else work on them the
way they work on Some and None. The reference solution here uses let-else
twice, because “bail out of this iteration on the boring case” is exactly what
let-else is for.
Notes
-
The
#[must_use]onResultis on the type, so it applies wherever aResultvalue is discarded, no matter which function produced it. -
One documented exception:
Result<(), E>whereEis an uninhabited type (a type with no values) does not fire, because it cannot be anErr. A footnote, not a loophole. -
let_underscore_must_useanddouble_must_useare the neighbouring lints; you will meet them properly later in this track. -
Integer division in Rust truncates towards zero, so
-9 / 2is-4, not-5.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.