We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 14 of 26
Refutability, and every place a pattern can appear
Compute the centroid, or coordinate-wise arithmetic mean, of a list of 2-D points.
pub fn centroid(points: Vec<(f64, f64)>) -> (f64, f64)
Return (0.0, 0.0) for an empty list. Otherwise, sum each coordinate in
input order, multiply each sum by 1.0 / points.len() as f64, and round
each result with the supplied round6 helper. For example, the points
(1.0, 1.0), (2.0, 2.0) and (4.0, 4.0) give (2.333333, 2.333333).
Assume inputs and intermediate arithmetic remain finite, including the
multiplication by one million in round6.
Practice destructuring in three places: a helper's parameter list, a
for binding, and a let binding for the helper's result. The starter
already uses all three; repair its handling of the empty input.
Patterns outside match
A pattern describes how to recognize or unpack a value. Tuple patterns can name the parts of a parameter, a loop item or a return value:
fn scale((x, y): (f64, f64), factor: f64) -> (f64, f64) {
(x * factor, y * factor)
}
fn main() {
let points = [(2.0, 4.0), (6.0, 8.0)];
let mut total = (0.0, 0.0);
for &(x, y) in &points {
total.0 += x;
total.1 += y;
}
let (x, y) = scale(total, 0.5);
assert_eq!((x, y), (4.0, 6.0));
}
Here for &(x, y) copies the two f64 values from each borrowed tuple.
The starter's for (x, y) in &points instead binds references to them.
Both work: f64 supports adding a borrowed f64 with +=.
Can this pattern fail to match?
An irrefutable pattern matches every value of the relevant type.
(x, y) can unpack any pair. A refutable pattern can fail:
Some(value) matches an Option containing a value, but not None.
The type matters. [a, b] is irrefutable for a two-element array, but
refutable for a slice whose length can vary. A new variable binding accepts
any value; a name that resolves to a constant or enum variant can instead
impose a matching condition.
Function and closure parameters, for bindings, and ordinary let
statements require irrefutable patterns. In the starter,
points.first() returns Option<&(f64, f64)>, so this statement fails with
E0005:
let Some(&(first_x, first_y)) = points.first();
The input might be empty. Handle that case explicitly before unpacking a
point, or return early for an empty list and sum all points from zero.
A let ... else is another option: its else branch must leave the
surrounding flow, for example with return.
Refutable patterns also work in if let, while let and match arms.
These constructs provide control flow for a failed match. This example
uses a pattern in a closure parameter too:
fn first_x_or_zero(points: &[(f64, f64)]) -> f64 {
let Some(&(x, _)) = points.first() else {
return 0.0;
};
x
}
fn main() {
assert_eq!(first_x_or_zero(&[]), 0.0);
assert_eq!(first_x_or_zero(&[(2.0, 3.0)]), 2.0);
let product = |(x, y): (i32, i32)| x * y;
let mut stack = vec![product((2, 3)), 4];
let mut sum = 0;
while let Some(value) = stack.pop() {
sum += value;
}
if let Ok(value) = "10".parse::<i32>() {
assert_eq!(sum, value);
}
}
Combining alternatives
An or-pattern combines alternatives with |. In a let binding or a
function or closure parameter, wrap a top-level or-pattern in parentheses.
This is a syntax requirement, separate from whether the combined pattern
covers every possible value.
enum Reading {
Measured(i32),
Estimated(i32),
}
fn value((Reading::Measured(n) | Reading::Estimated(n)): Reading) -> i32 {
n
}
fn main() {
let reading = Reading::Measured(7);
let (Reading::Measured(n) | Reading::Estimated(n)) = reading;
assert_eq!(n, 7);
assert_eq!(value(Reading::Estimated(9)), 9);
}
Each alternative alone is refutable, but together they cover both variants. They bind the same name with the same type and binding mode. Adding a third variant would make this combined pattern refutable until it was handled too.
Rounding for this exercise
round6(value) computes (value * 1e6).round() / 1e6. Rust's round
rounds halfway cases away from zero. This is a task-specific output rule;
it does not remove all floating-point error or make summation independent
of input order. In other programs, choose precision and error tolerances
to suit the calculation instead of automatically rounding at every boundary.
Submissions must compile, pass the value tests, and pass Clippy with warnings treated as errors. The value tests check results; review your own code for the three requested uses of destructuring.
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.