We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Rust: zero to hero step 9 of 342
checked, wrapping, saturating, overflowing
Add the values in a slice from left to right. Return None as soon as an
addition would put the running total outside the i32 range.
pub fn safe_sum(values: &[i32]) -> Option<i32>
Start at zero. If every addition fits, return Some(total). The empty slice
returns Some(0).
| Input | Result |
|---|---|
[1, 2, 3] |
Some(6) |
[i32::MAX, 1] |
None |
[i32::MIN, -1] |
None |
[i32::MAX, 1, -1] |
None |
The last case matters: the final mathematical sum fits, but an intermediate sum does not. Do not add everything in a wider type and check only at the end. Each prefix of the input must have a representable total.
Choose the overflow behavior
The starter uses total += v. If overflow checks are enabled, an overflowing
addition panics. If they are disabled, it wraps. Neither behavior returns the
None required here. Cargo's default development and release profiles differ
in their overflow-check setting, but that setting can be overridden.
Integer methods let you choose a behavior explicitly. Here are four useful addition methods:
let n = 250u8;
assert_eq!(n.checked_add(10), None);
assert_eq!(n.wrapping_add(10), 4);
assert_eq!(n.saturating_add(10), 255);
assert_eq!(n.overflowing_add(10), (4, true));
checked_addreturnsNoneif the result does not fit. Use it here to let the caller handle overflow.wrapping_addcomputes modulo the integer type's range. Foru8, that means modulo 256. This is useful when an algorithm requires modular arithmetic.saturating_addstops at the type's minimum or maximum. Those limits are not necessarily your application's limits: a percentage stored inu8still needs an explicit upper bound of 100.overflowing_addreturns the wrapped result together with a flag saying whether overflow occurred. Wider arithmetic can use that flag as a carry.
These methods behave consistently whether overflow checks are enabled or
disabled. They also have subtraction and multiplication counterparts, but
not every operation has every form. See the
integer method documentation
for the operation you need. In particular, checked_shl checks the shift
count; it does not reject a shift just because nonzero bits are lost.
Keep the modulus in mind when using wrapping arithmetic. Wrapping an integer is not automatically the same as wrapping an index around a buffer of an arbitrary length.
Represent success or overflow with Option
checked_add returns Option<i32>. Some(value) contains a valid result;
None represents overflow. Both are variants of the standard library's
Option enum, whose basic shape is:
enum Option<T> { Some(T), None }
Option<i32> and i32 are different types. You need to handle the option
before using its contained integer. For this exercise, overflow should end
the calculation, so return None rather than unwrapping it and panicking.
Zero is a valid sum and is represented by Some(0), not None.
Propagate None with ?
This line updates the total only when the addition succeeds:
total = total.checked_add(v)?;
Here, ? extracts the value from Some(value). On None, it immediately
returns None from safe_sum. The function's Option<i32> return type
supports that early return. If you put the expression inside a closure,
propagation is from that closure instead, so its return type must support it.
For [i32::MAX, 1, -1], the steps are:
| Next value | Result of checked addition | Action |
|---|---|---|
i32::MAX |
Some(i32::MAX) |
Update total |
1 |
None |
Return None |
-1 |
Not evaluated | Already returned |
If the loop finishes, wrap the total in Some. The same rule covers negative
overflow: i32::MIN.checked_add(-1) also returns None.
As a separate experiment, consider a function that uses saturating_add at
each step and returns an i32. This changes the contract. For
[i32::MAX, 1, -1], it returns i32::MAX - 1: once an intermediate value is
clamped, a later subtraction cannot recover the lost amount. Step-by-step
saturation is not the same as clamping the final mathematical sum.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.