We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership II: Borrowing and the Borrow Checker step 10 of 24
&str not &String, &[T] not &Vec<T> — deref coercion and ptr_arg
pub fn longest_common_prefix(words: &[String]) -> String
Return the longest prefix shared by every word, measured in characters
(chars), not bytes. An empty list gives "".
[] -> ""
["flower","flow","flight"] -> "fl"
["dog","cat"] -> ""
["abc"] -> "abc"
["héllo","héro"] -> "hé"
Character-wise matters: "héllo" and "héro" share the two characters h
and é, and é is two bytes. A byte-wise comparison of two different
accented letters could stop halfway through a multi-byte character, and
slicing there panics. Comparing chars makes that impossible.
The whole function is already written for you and it passes every test. It still fails the grade.
The lint
error: writing `&String` instead of `&str` involves a new object where a slice will do
--> src/lib.rs:1:23
|
1 | fn shared_prefix_len(a: &String, b: &String) -> usize {
| ^^^^^^^
= note: `-D clippy::ptr-arg` implied by `-D warnings`
clippy::ptr_arg is warn-by-default, which under -D warnings means your
submission does not pass. It is the highest-frequency API-design lesson in
Rust, and this problem exists so that the grader teaches it to you
mechanically rather than a reviewer teaching it to you socially.
Why &String is a worse parameter type than &str
A String is three words: pointer, length, capacity — plus a heap allocation.
A &String is a pointer to those three words. A &str is two words:
pointer and length, pointing directly at the bytes.
&String ──▶ [ ptr | len | cap ] ──▶ "hello"
&str ──────────────────────────▶ "hello" (+ len)
So &String is a pointer chase you did not need. But indirection is the small
problem. The big problem is that &String narrows who can call you:
fn takes_string(s: &String) { }
fn takes_str(s: &str) { }
takes_string("literal"); // no. a literal is a &'static str.
takes_str("literal"); // yes
takes_str(&some_string); // yes
takes_str(&big[3..8]); // yes — a sub-slice, no allocation
A caller holding a literal, a slice, or text borrowed from a buffer has to
allocate a String just to call your &String function. You have made
every caller pay for a constraint you did not need.
The same argument, identically, for &Vec<T> versus &[T], and for
&PathBuf versus &Path. Same lint, same reasoning.
The magic that makes the fix free: deref coercion
Here is the part that makes this lesson land. Change the helper’s parameters
from &String to &str and then look at the call site:
n = n.min(shared_prefix_len(first, w)); // `first` and `w` are &String
You do not have to touch it. Not one character.
That is deref coercion: when a function expects &U and you supply &T
where T: Deref<Target = U>, the compiler silently inserts the dereference.
String: Deref<Target = str>, so &String becomes &str at the boundary,
free, at compile time. Likewise Vec<T>: Deref<Target = [T]>, so &Vec<i64>
becomes &[i64], and &mut Vec<i64> becomes &mut [i64].
This is why the advice is unambiguous rather than a trade-off. Widening your
parameter from &String to &str costs your existing callers nothing and
gains you every caller who has a &str already. There is no other side to the
argument.
It is also the answer to a mystery you have already met: why words.join(" ")
works when join is defined on [T] and not on Vec<T>, and why
some_string.trim() works when trim is defined on str. Method lookup
walks the deref chain. It has been quietly doing this since your first day.
When &String is not wrong
Clippy is a linter, not a proof system, and it knows about the exception. If
your function body genuinely uses String-only capability — capacity,
reallocation, ownership transfer — then &String (or, more likely, &mut String or String) is the correct type and clippy will not fire. The lint is
suppressed when the body needs the concrete type. It has historically had
false positives around this (rust-clippy#8482 is the canonical thread), which
is worth knowing so that you evaluate the suggestion rather than obeying it
reflexively.
In this problem the helper only reads characters, so &str is plainly right
and clippy is plainly correct.
About the outer signature
longest_common_prefix(words: &[String]) takes a slice of owned Strings —
the collection is borrowed, the elements are owned by the caller’s vector.
That is normal and correct. It is &Vec<String> that would have been flagged.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.