We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Generics and Traits step 4 of 24
where clauses and the bound you cannot write inline
A trait bound is a requirement on a type, such as T: Display, which
says "T must implement the Display trait". Rust lets you write bounds in two
places. These two signatures are equivalent:
pub fn summarise<T: Display>(items: Vec<T>) -> String
pub fn summarise<T>(items: Vec<T>) -> String
where
T: Display,
The first puts the bound inline, next to the generic parameter in the
angle brackets. The second puts it in a where clause after the
signature. For a simple bound like this one, the choice is about readability.
What a where clause adds
An inline bound is attached to a generic parameter you declare inside
<...>. A where clause can put a bound on any type, including types built
from those parameters:
pub fn copy_all<T>(v: &Vec<T>) -> Vec<T>
where
Vec<T>: Clone, // a bound on Vec<T>, not on T
{
v.clone()
}
There is no inline position for Vec<T>: Clone, because Vec<T> is not a
parameter in the angle brackets.
A related case is an associated type: a type that a trait implementation
chooses. IntoIterator has one called Item, the type of the values you get
when you loop over the collection. If I: IntoIterator, then I::Item is
that type. You cannot list I::Item as a parameter in the angle brackets
(<I: IntoIterator, I::Item: Display> is a syntax error), but you can bound
it in a where clause:
where
I: IntoIterator,
I::Item: Display,
Since Rust 1.79 you can also bound it inline, nested inside I's own bound:
<I: IntoIterator<Item: Display>>. Both forms mean the same thing and both
pass this exercise. This exercise uses the where form.
Your task
pub fn summarise<T>(items: Vec<T>) -> String
where
T: Display,
pub fn summarise_any<I>(items: I) -> String
where
I: IntoIterator,
I::Item: Display,
pub fn run(nums: Vec<i64>, words: Vec<String>) -> Vec<String>
Both summarisers produce the same format: the number of items, the text
" items: ", then each item's Display output in input order, separated by
", ".
[1, 2, 3]gives"3 items: 1, 2, 3".- The word is always
items, even for one item:[-42]gives"1 items: -42". - An empty input gives
"0 items: ", with a trailing space, because the same format string is used for every length. - Numbers use the normal decimal form of
i64, with a leading-for negatives. Strings appear exactly as given, with no quotes or escaping, so["", ""]gives"2 items: , ". Non-ASCII text such as"héllo"is copied unchanged.
run returns [summarise(nums), summarise_any(words)]. The tests only call
run, but summarise_any should accept any iterable whose items implement
Display, not just Vec<String>.
The two problems in the starter
The grade requires the code to compile, pass the tests and pass
cargo clippy -- -D warnings, which turns every Clippy warning into an error.
The starter fails for two reasons.
1. A bound is written in two places. summarise has <T: Display> and
also where T: Display. The compiler accepts that, but Clippy's
multiple_bound_locations lint, which is enabled by default, reports it:
error: bound is defined in more than one place
|
2 | pub fn summarise<T: Display>(items: Vec<T>) -> String
| ^
3 | where
4 | T: Display,
| ^
The lint fires whenever one parameter has bounds in both locations, even if the two bounds name different traits. Keep one location and delete the other.
2. summarise_any does not know what its items are. I: IntoIterator
says the argument can be looped over, but says nothing about the type of
each item. The body calls .to_string() on each item, which needs Display,
so the compiler rejects it:
error[E0599]: `<I as std::iter::IntoIterator>::Item` doesn't implement `std::fmt::Display`
= note: the following trait bounds were not satisfied:
`<I as std::iter::IntoIterator>::Item: std::fmt::Display`
which is required by `<I as std::iter::IntoIterator>::Item: std::string::ToString`
<I as std::iter::IntoIterator>::Item is the fully written-out name of
I::Item: the Item type of I's IntoIterator implementation. The
fix is to add the bound the note asks for.
Clippy lints you will not see here
Clippy also has type_repetition_in_bounds and trait_duplication_in_bounds,
which look for repeated bounds. Both are in the nursery group, which is
disabled by default, so they do not run in this grader. The lint that
affects this exercise is multiple_bound_locations.
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.