In Rust, one often sees functions that take &str
as a parameter.
fn foo(bar: &str) {
println!("{}", bar);
}
When calling functions like this, it is perfectly fine to pass in a String
as an argument by referencing it.
let bar = String::from("bar");
foo(&bar);
Strictly speaking, the argument being passed is an &String
and the function is expecting an &str
, but Rust (as one would hope) just figures it out and everything works fine. However, this is not the case with match statements. If I try and use the same bar
variable as before in a match statement, the naive usage will not compile:
match &bar {
"foo" => println!("foo"),
"bar" => println!("bar"),
_ => println!("Something else")
};
Rustc complains that it was expecting an &str
but received an &String
. The problem and solution are both very obvious: just borrow bar
more explicitly with .as_str()
. But this brings me to the real question: why is this the case?
If Rust can figure out that an &String
trivially converts to an &str
in the case of function arguments, why can't it do the same thing with match statements? Is this the result of a limitation of the type system, or is there hidden unsafety in fancier borrowing with match statements? Or is this simply a case of a quality of life improvement getting integrated into some places but not others? I'm sure someone knowledgeable about type systems has an answer, but there seems to be very little information about this little quirk of behavior on the internet.
The technical reason it doesn't work is because the match
scrutinee is not a coercion site. Function arguments, as shown in your foo(&bar)
example, are possible coercion sites; and it allows you to pass a &String
as a &str
because of Deref
coercion.
A possible reason why its not a coercion site is that there's no clear type that it should be coerced to. In your example you'd like it to be &str
since that matches the string literals, but what about:
match &string_to_inspect {
"special" => println!("this is a special string"),
other => println!("this is a string with capacity: {}", other.capacity()),
};
One would like the match
to act like &str
to match the literal, but because the match is on a &String
one would expect other
to be a &String
as well. How to satisfy both? The next logical step would be for each pattern to coerce as required, which has been much desired... but it opens a whole can of worms since Deref
is user-definable. See deref patterns from the Rust lang-team for more info.