I'm new to Rust (1.31) and I would like to understand a simple piece of code that does not compile:
fn main() {
s = String::from("foo");
match s {
"foo" => {
println!("Yes");
}
_ => {
println!("No");
}
}
}
The error associated is :
10 | "foo" => {
| ^^^^^ expected struct `std::string::String`, found reference
After this error, I decided to change the code to :
fn main() {
let s = String::from("foo");
match s {
String::from("foo") => {
println!("Yes");
}
_ => {
println!("No");
}
}
}
By doing so, I was hoping to have the correct type, but it is not the case :
10 | String::from("foo") => {
| ^^^^^^^^^^^^^^^^^^^ not a tuple variant or struct
I am quite puzzled with this message from the compiler, at the end I managed to make it work by implementing :
fn main() {
let s = String::from("foo");
match &s as &str {
"foo" => {
println!("Yes");
}
_ => {
println!("No");
}
}
}
However, I do not understand the underlying mechanisms that make this solution the right one and why my second example does not work.
To conclude, string comparison is quite easy in Rust. The only thing you have to do is convert the string literal into &str. Then you will be able to match a string against a string literal.
Patterns are a special syntax in Rust for matching against the structure of types, both complex and simple. Using patterns in conjunction with match expressions and other constructs gives you more control over a program's control flow.
Rust has an extremely powerful control flow construct called match that allows you to compare a value against a series of patterns and then execute code based on which pattern matches.
In easy words, String is datatype stored on heap (just like Vec ), and you have access to that location. &str is a slice type. That means it is just reference to an already present String somewhere in the heap. &str doesn't do any allocation at runtime.
The first example doesn't work, because s
is of type String
, which is a string variant that owns the data in it. It is matched against a string literal (which can be be used as type &str
). match doesn't understand how to compare those two different types, so it errors.
However String
dereferences to &str
, by implementing Deref<Target=str>
, which means references to String
can be used where a &str
is required, e.g. for comparing it to one. That's what happens in the third example. By creating the reference &s
, the implicit deref can happen, and the two types get comparable.
You can achieve the same thing with a little less magic by using the explicit method which creates a &str
from String
:
fn main() {
let s = String::from("foo");
match s.as_str() {
"foo" => {
println!("Yes");
},
_ => {
println!("No");
}
}
}
The second example tries to make things comparable making String
the common type instead of &str
. It doesn't work, because match expects a pattern on the left side, and not a function call which creates a new struct (and which also allocates behind the scene). Even if it would work (e.g. by moving the String
creation outside of the match), the approach would be less desirable, because the new String
would require a memory allocation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With