We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ground Rules: Values, Types, Control Flow step 18 of 24
Return minimum, maximum and mean as a tuple
Complete the function so it returns (minimum, maximum, mean) for a slice:
pub fn min_max_mean(values: &[i32]) -> (i32, i32, f64)
| Input | Result |
|---|---|
[1, 2, 3] |
(1, 3, 2.0) |
[1, 2] |
(1, 2, 1.5) |
[-4, -2] |
(-4, -2, -3.0) |
[5] |
(5, 5, 5.0) |
[] |
(0, 0, 0.0) |
The empty result is this function's convention: an empty collection has no ordinary minimum, maximum or arithmetic mean. Count every element, including duplicates, when computing the mean. Do not round it to an integer.
The starter already handles empty input and calculates the minimum, maximum and sum. Fix its final expression so the third tuple field has the promised type and keeps the fractional part of the mean.
A tuple carries several values
A tuple has a fixed number of fields, each with its own type. The fields may
have different types, as they do in (i32, i32, f64). Their order matters:
returning maximum before minimum would violate this function's contract.
let pair: (i32, f64) = (7, 1.5);
assert_eq!(pair.0, 7);
assert_eq!(pair.1, 1.5);
let single = (7,); // The comma makes this a one-element tuple.
assert_eq!(single.0, 7);
Tuple fields use fixed positions such as .0 and .1. A runtime variable
cannot replace the field number, and a nonexistent field is a compile error.
() is the empty tuple, also called unit.
Destructuring assigns names to the fields in one binding:
let values = [1, 2, 3];
let (min, max, mean) = min_max_mean(&values);
assert_eq!((min, max, mean), (1, 3, 2.0));
let (_, _, mean_only) = min_max_mean(&values);
assert_eq!(mean_only, 2.0);
The left side is a pattern. Each _ ignores that field. Positional access
and destructuring are both useful; choose names when they make the result
easier to read. A struct is another option when named fields would clarify
an API, but this exercise asks for a tuple.
Divide in floating point
The starter ends with:
(min, max, sum / values.len())
sum is i64, while the length is usize. Rust does not provide that mixed
integer division. Converting the length to i64 alone would still produce
an integer, not the f64 required by the return type.
Convert both operands to f64 before dividing. For [1, 2], integer
division would truncate 3 / 2 to 1; converting that result afterward
cannot recover 1.5. Negative integer division also truncates toward zero.
Keep the wider accumulator
The sum can exceed i32 even though every input fits. Two values equal to
i32::MAX sum to 4294967294, for example. An i32 accumulator would overflow,
whereas the supplied i64 accumulator holds that sum exactly.
Convert each value before adding it to the wider sum. Widening a result after
an overflowing addition is too late. Exercise inputs have running sums that
fit in i64; a wider accumulator is not unlimited protection for arbitrarily
large inputs.
Converting a large integer to f64 can round, and the mean may not have an
exact binary floating-point representation. The grader allows a numerical
tolerance. Return the calculated mean without extra decimal rounding.
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.