fn main() {
let strA = "a";
let result;
{
let strB = "abc";
result = longest(strA, strB); // Will return strB
}
println!("The longest string is {}", result); // result now point to strB!!
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
As I got from the Rust book
'a
will get the concrete lifetime that is equal to the smaller of the lifetimes ofx
andy
So why strB
is now visible outside of its scope?
Lifetimes are what the Rust compiler uses to keep track of how long references are valid for. Checking references is one of the borrow checker's main responsibilities. Lifetimes help the borrow checker ensure that you never have invalid references.
A lifetime is a construct the compiler (or more specifically, its borrow checker) uses to ensure all borrows are valid. Specifically, a variable's lifetime begins when it is created and ends when it is destroyed. While lifetimes and scopes are often referred to together, they are not the same.
Rust uses lifetime parameters to avoid such potential run-time errors. Since the compiler doesn't know in advance whether the if or the else block will execute, the code above won't compile and an error message will be printed that says, “a lifetime parameter is expected in compare 's signature.”
Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Static items may be placed in read-only memory if the type is not interior mutable. Static items do not call drop at the end of the program.
That's because all string literals have 'static
lifetime. From the rust book:
One special lifetime we need to discuss is 'static, which means that this reference can live for the entire duration of the program. All string literals have the 'static lifetime, which we can annotate as follows:
let s: &'static str = "I have a static lifetime.";
The text of this string is stored directly in the program’s binary, which is always available. Therefore, the lifetime of all string literals is 'static
Fixed example:
fn main() {
let strA = "a".to_string();
let result;
{
let strB = "abc".to_string();
result = longest(&strA, &strB); // Will return strB
}
println!("The longest string is {}", result); // compile error
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
Now produces a compiler error as expected:
error[E0597]: `strB` does not live long enough
--> src/main.rs:7:33
|
7 | result = longest(&strA, &strB); // Will return strB
| ^^^^^ borrowed value does not live long enough
8 | }
| - `strB` dropped here while still borrowed
9 |
10 | println!("The longest string is {}", result); // result now point to strB!!
| ------ borrow later used here
playground
Rust was not "ignoring" the lifetimes of the string variables in your initial example. When you set a variable to a string literal that literal gets hardcoded into the executable binary and gets a 'static
lifetime which means it's valid for the entire duration of the program. If we add explicit type annotations to your initial example it should become clear why it compiles and works:
fn main() {
let strA: &'static str = "a";
let result;
{
let strB: &'static str = "abc";
result = longest(&strA, &strB); // returns 'static str
}
println!("The longest string is {}", result); // prints result
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
However when we call to_string()
on a string literal we create an owned and heap allocated String
which lifetime's is non-static and is scoped to whatever block its in, hence making the change makes the program no longer compile as expected.
the lifetime 'a
refers to the lifetime of the string buffer str
not the reference to that buffer. So the lifetime of the &str
strB
is within the block. However, "abc"
is a constant string. This means that the storage of the str buffer has 'static
lifetime, meaning it is guaranteed to outlast any other lifetime. As such it is valid for result to reference that buffer even after strB
no longer references it.
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