The idea is that `shortest()` returns a reference to the shorter of the two
strings referenced by its arguments, but *without* allocating a new string.
Let's de-sugar `main()` so we can see the implicit lifetimes:
`print_shortest()` has been renamed to `shortest()`, which instead of printing,
now returns the shorter of the two strings. It does this using only references
for efficiency, avoiding the need to re-allocate a new string to pass back to
`main()`. The responsibility of printing the result has been shifted to `main()`.
Let's again de-sugar `main()` by adding explicit scopes and lifetimes:
```rust,ignore
fn main() {
'a {
's1 {
let s1 = String::from("short");
b' {
let s2r: &'b = &'b String::from("a long long long string");
'c {
// Annonymous scope for the borrow of s1
println!("{}", shortest(&'c s1, s2r));
'res {
let res: &'res str;
's2 {
let s2 = String::from("a long long long string");
res: &'res: str = shortest(&'s1 s1, &'s2 s2);
println!("{}", res);
}
}
}
}
```
Now we see that the references passed as arguments to `shortest()`, i.e. `&s1`
and `&s2`, actually have different lifetimes (`&'b` and `&'c` respectively), since the borrows occur in different scopes.
However, the signature of
`shortest()` requires that these two references (and also the returned
reference, which has lifetime `'c`) have the same lifetime. So how does the
compiler make sure this is the case?
Again, at the call-site of `shortest()` the comipiler needs to check the
consistency of the arguments in the caller with the signature of the callee.
The signature of `shortest()` fisrt says that the two reference arguments have
the same lifetime, which can be prove ok in the same way as before, thus giving
us:
At the call-site of `shortest()`, the compiler must try to *convert* the lifetimes of
the references marked `&'a` in the signature of `shortest()`
into a single compatible lifetime. This new lifetime must be shorter than, or equally as
long as, all three of the original reference lifetimes involved. In other words, we must convert to the shortest of the three lifetimes to `&'c`. Conversion from a reference `&'o` can be converted to to `&'p` if `'o` lives at least as long as `'p`, therefore:
```rust,ignore
res: &'res = shortest(&'s2 s1, &'s2 s2);
```
* `The first argument &'c s1` already has lifetime `&'c`, so we don't have to do anything here.
* `&'b` outlives `&'c`, so we can convert `s2r: &'b` to `s2r: &'c`.
* The returned reference has lifetime `&'c` already.
But we now have the additional reference to check. We must now prove that the
returned reference can have the same lifetime as the arguments of lifetime
'&'s2'. This brings us to a second rule:
After conversion, the call-site satisfies the signature of `shorter()`, we have
proven the lifetimes in this program to be consistent, and therefore the
compiler accepts the program.
> The return value of a function `&'r T` can be converted to an argument `&'s T`
> if the lifetime of `&'r T` is equal or shorter than `&'s T`.
Now consider a slight variation on `main()` like this:
To make our program compile, we would have to subsitute `res: &'res` for `res:
&'s2`, but we can't since `&'res` in fact out-lives `&'s2`. This program is in
fact inconsistent and the compiler rightfully rejects the program because we
try make a reference (`&'res`) which outlives one of the values it refer to
(`&'s2`).
[Formally, function return values are said to be *contravariant*, the opposite
of *covariant*.]
How can we fix this porgram? Well if you were to swap the `let s2 = ...` with
the `res = ...` line, you would have:
```rust,ignore
fn main() {
let s1 = String::from("short");
let res;
let s2 = String::from("a long long long string");
let res;
res = shortest(&s1, &s2);
println!("{}", res);
}
```
De-sugared it looks like this:
Which de-sugars to:
```rust,ignore
fn main() {
'a {
's1 {
let s1 = String::from("short");
'b {
let res: &'b str;
'c {
let s2 = String::from("a long long long string");
'd {
// Annonymous scope for the borrows of s1 and s2
// Assigning to the outer scope causes s1 and s2 to have 'b
res: &'b = shortest(&'d s1, &'d s2);
println!("{}", res);
}
's2 {
let s2 = String::from("a long long long string");
'res {
let res: &'res str;
res: &'res str = shortest(&'s1 s1, &'s2 s2);
println!("{}", res);
}
}
}
}
```
XXX: Something is wrong. The above program does not compile, so we should be
able to show that the lifetimes are inconsistent. To do so we would to be have
to show that we can't convert `&'b` to `&'d`, but since `&'b` outlives `&'d`,
we can. Hrmm.
Then at the call-site of `shortest()`:
* `&'s1 s1` outlives `&'s2 s2`, so we can replace the first argument with `&'s2 s1`.
* `&'res str` lives shorter than `'&s2`, so the return value lifetime can become `res: &'s2 str`
Leaving us with:
```rust,ignore
res: &'s2 str = shortest(&'s2 s1, &'s2 s2);
```
Which matches the signature of `shortest()` and thus this compiles.
Intuitively, the return reference can't point to a freed value as the values