We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 12 of 26
Slice and array patterns, and rest @ ..
Write two functions. Both are graded.
pub fn shrink(tokens: Vec<String>) -> String
pub fn windows_sum(v: Vec<i64>) -> Vec<i64>
shrink
shrink combines a list of strings from both ends toward the middle:
| tokens | result |
|---|---|
[] |
empty |
["a"] |
a |
["a","b"] |
a+b |
["a","b","c"] |
a(b)c |
["a","b","c","d"] |
a(b+c)d |
["a","b","c","d","e"] |
a(b(c)d)e |
The rule has four cases:
- No tokens: return the literal text
empty. - One token: return that token unchanged.
- Two tokens: return them joined by
+, as ina+b. - Three or more tokens: return the first token, then
(, then the result of applyingshrinkto the tokens strictly between the first and the last, then), then the last token.
Tokens are used exactly as given. There is no escaping or trimming, and a
token may itself be empty or contain (, ), + or non-ASCII text. For
example, ["", "x"] gives +x, and ["empty"] gives empty, the same
text as an empty list.
Six tokens show the recursion one level deeper: the middle of
["a","b","c","d","e","f"] is ["b","c","d","e"], which gives b(c+d)e,
so the full result is a(b(c+d)e)f. Each level of recursion removes two
tokens, so the middle part is never empty when the three-or-more rule
applies.
windows_sum
windows_sum returns the sum of every run of three consecutive elements, in
order from left to right. [1,2,3,4] has the runs [1,2,3] and [2,3,4],
so it gives [6, 9]. An input with fewer than three elements gives [].
Values may be negative or zero. The tests only use inputs where each sum of
three values fits in i64; the reference solution does not check for
overflow.
Write both functions without indexing: no v[i] and no index arithmetic
such as v.len() - 1. The tests check outputs, not how you got them, so this
is the point of the exercise rather than something the grader detects.
Slice patterns
A slice pattern is a pattern written with square brackets. It matches a
slice ([T]) or an array ([T; N]) by its length and its elements:
match tokens {
[] => ..., // exactly zero elements
[only] => ..., // exactly one
[a, b] => ..., // exactly two
[first, mid @ .., last] => ..., // two or more; mid may be empty
}
Inside a slice pattern, .. matches any number of elements, including
none. Writing name @ .. binds those elements to name as a sub-slice. In
the last arm, mid is everything between the first and the last element.
Arms are tried from top to bottom. [first, mid @ .., last] also matches a
two-element slice (with an empty mid), so the [a, b] arm has to come
before it. If it came after, a two-token input would produce a(empty)b.
The alternative is a len() check followed by tokens[0],
tokens[tokens.len() - 1] and a range such as &tokens[1..tokens.len() - 1].
Each of those expressions panics if its index or range is out of bounds, so
every one depends on the length check being right. A slice pattern combines
the length check and the element access, so it cannot fail with an
out-of-bounds panic. The compiler also checks that the arms cover every
length, and reports error E0004 if they do not.
A slice pattern can contain at most one ... With two, such as
[a, .., b, ..], the compiler cannot tell how many elements each one should
take, and it rejects the pattern with the error
`..` can only be used once per slice pattern.
The pattern [first, rest @ ..] splits a slice into its first element and
everything after it, which is a common shape for recursive functions over a
list.
The starter's compile error
The starter code does not compile. Its first error is:
error[E0529]: expected an array or slice, found `Vec<String>`
Slice patterns match only arrays and slices, and Vec<T> is neither. A
Vec<T> is a struct that owns a buffer and can produce a &[T] through the
Deref trait. Rust inserts that conversion automatically in some places,
such as method calls and function arguments, but not when choosing what a
match compares against. You have to produce the slice yourself:
match v.as_slice() { ... } // or: match &v[..] { ... }
The recursion is simpler if the helper takes &[String] instead of
Vec<String>. Then mid is already a &[String] and can be passed
straight to the recursive call. Taking a sub-slice does not copy the tokens
or allocate a new vector; the only allocations are the String results that
format! builds.
Each level of recursion uses one stack frame and removes two tokens, so the recursion depth is about half the number of tokens. The tests use short lists.
What the bindings are
When a pattern of non-reference form such as [first, .., last] matches a
reference such as &[String], Rust's default binding modes bind each name
by reference. first and last therefore have type &String, not
String. This is the rule in every edition since Rust 1.26.
That is why the one-token arm calls only.clone() to return an owned
String. In windows_sum, v.windows(3) yields &[i64] sub-slices, so a
pattern [a, b, c] binds three &i64 values. a + b + c still works
because the standard library implements Add for references to integers:
a + b produces an i64, and adding &i64 to that i64 also produces an
i64.
The type of each window is &[i64], not [i64; 3], so the compiler does
not know that every window has exactly three elements. A match on a window
therefore needs a fallback arm such as _ => 0, or it is rejected with
E0004. With windows(3) that arm never runs.
Range patterns inside slice patterns
A range pattern with no upper end, such as 1.., must be put in parentheses
when it is an element of a slice pattern: [(1..), ..] compiles on stable
Rust. Without parentheses, [1.., ..] is rejected with error E0658,
because that form is still an unstable feature.
A note on indexing lints
Clippy has a lint, clippy::indexing_slicing, that flags indexing and
slicing such as v[i] and &v[a..b] on a Vec or slice, because each one
can panic. (It does not flag a constant index or range into an array that
is known to be in bounds.) It is in Clippy's restriction
group and is off unless you enable it, because it would flag a lot of
ordinary, correct code. It is not enabled for this exercise. An older lint,
clippy::match_on_vec_items, has been removed; Clippy's removal message
says that indexing_slicing covers indexing and slicing on Vec.
Your submission must compile, pass the tests, and produce no warnings under
cargo clippy -- -D warnings.
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.