Skip to content

← Errors Are Values step 7 of 24

Easy Primitives

Result, and why it is #[must_use]

Evaluate division expressions such as "10/2". Return one string per expression, in input order. A failure must not stop later expressions.

Implement these functions:

pub fn checked_div(a: i32, b: i32) -> Result<i32, String>
pub fn apply(ops: Vec<String>) -> Vec<String>

Division results

checked_div returns the integer quotient in Ok. Integer division truncates towards zero: -9 / 2 produces -4, and 1 / 2 produces 0. The division does not need to be exact.

Return Err("division by zero".to_string()) when b is zero. For i32::MIN / -1, return Err("division overflow".to_string()): the positive quotient cannot fit in i32. These are the two failure cases to handle before ordinary integer division. Neither should panic.

Parse and format each expression

apply splits at the first /, trims whitespace from both sides, and parses each side as a decimal i32. A leading sign is allowed; an empty side, extra slash, decimal point or out-of-range integer fails parsing. Check these cases in order:

Condition Output
No slash "error: malformed"
Either side fails to parse "error: not a number"
Both parse, but the divisor is zero "error: division by zero"
Both parse, but the quotient overflows "error: division overflow"
Successful division The quotient as a decimal string

For example, "x/0" is a parsing error, "1/2/3" is a parsing error, and " 12 / 4 " produces "3". Empty input produces an empty vector. checked_div returns the error text without a prefix; apply adds "error: " once when formatting a failed division.

Handle both Result variants

Result<T, E> represents either Ok(T) or Err(E). Here, the success value is an i32 and the error is a String. Returning Err is ordinary value construction; it does not itself panic or unwind the stack.

A match can turn both variants into output strings:

match checked_div(a, b) {
    Ok(value) => out.push(value.to_string()),
    Err(error) => out.push(format!("error: {error}")),
}

The starter calls checked_div but ignores its result and always appends "0". Replace that behavior with an output based on the returned variant.

What #[must_use] checks

Result has the #[must_use] attribute. A bare call whose returned Result is discarded, such as the starter's checked_div(a, b);, triggers the compiler's unused_must_use warning. Warnings are errors in this exercise's grading checks.

An explicit let _ = checked_div(a, b); suppresses that particular warning. It still discards the result, so it does not solve this exercise. The attribute is a diagnostic aid, not proof that every error has been handled: a program can explicitly discard a result or pass it elsewhere without examining it.

Give the error useful information

The starter's Result<i32, ()> can indicate failure but cannot distinguish its causes. It also triggers Clippy's result_unit_err lint for public functions. Use the required String error type to carry the two messages.

Option<T> is useful when the caller only needs a value or its absence. Result<T, E> expresses success or failure, with an error representation chosen for the API. Neither is universally better. A larger API may use an error enum so callers can distinguish failures without comparing strings.

Use continue after appending a malformed-input or parsing-error message. That skips the current expression and keeps the rest of the batch running. Passing requires compilation, correct outputs and Clippy with warnings treated as errors.