fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {result}");
}
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
I understand that the reason Rust does not accept this is because the elision rules don't cover this case, but I don't understand why don't they cover.
In other words, it seem obvious (to my naive eyes) that both variables that were sent in the function will outlive the return value so at least theoretically this could be covered in the elision rules.
I feel, probably incorrectly, that this does not really show why the problem that lifetime specifiers help solve.
There isn’t a simple mechanical rule that will let the compiler (or the reader of the code) know which lifetimes should match just by looking at the elided function signature. There are many possible correct answers depending on the programmer’s intent. Therefore, we ask the programmer to explicitly state that intent.
It's on the caller side, imagine you had code like this:
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
drop(string1); // borrow checker will flag the call below because of this
println!("The longest string is {result}");
}
fn longest(x: &str, y: &str) -> &str { /* as in OP */ }
I'd guess that rust technically could default to the most restictive lifetime in all cases. It's not what it does as the designers prefered to have code explicit rather then implicitly choose one of multiple options. (something that you'll see quite often as a design choice in rust)
In case this is the confusion: Rust lifetimes ('_) in types are generally about how long some place like a variable is borrowed, and not the variable's liveness or drop scope.[1] And you usually want things to get borrowed for the shortest duration possible, to minimize the restrictions on what your other code can do.
Here's another example. In the version that compiles, the lifetime of the type of the &two that was passed to first ends before the push_str, but the (lifetime in the type of the) returned value is alive beyond that.
In the versions that don't compile, there's a use after the push_str that needs the lifetime to stay alive (because the input lifetime is tied to the lifetime of result in those cases). But two being borrowed conflicts with taking a &mut to call push_str; hence you get an error.
I think this was the missing piece. Though I think in my example, instead of calling drop I'll assign a new value to string1. (for which I'll have to make it mutable as well.