2022-10-29 05:10:38 -05:00
# Advance lifetime
2022-03-28 19:17:16 -06:00
2022-03-30 03:08:16 -06:00
## Trait Bounds
Just like generic types can be bounded, lifetimes can also be bounded as below:
- `T: 'a` οΌ all references in `T` must outlive the lifetime `'a`
- `T: Trait + 'a` : `T` must implement trait `Trait` and all references in `T` must outlive `'a`
**Example**
```rust,editable
use std::fmt::Debug; // Trait to bound with.
#[derive(Debug)]
struct Ref< 'a, T: 'a>(& 'a T);
// `Ref` contains a reference to a generic type `T` that has
// an unknown lifetime `'a` . `T` is bounded such that any
// *references* in `T` must outlive `'a` . Additionally, the lifetime
// of `Ref` may not exceed `'a` .
// A generic function which prints using the `Debug` trait.
fn print< T > (t: T) where
T: Debug {
println!("`print`: t is {:?}", t);
}
// Here a reference to `T` is taken where `T` implements
// `Debug` and all *references* in `T` outlive `'a` . In
// addition, `'a` must outlive the function.
fn print_ref< 'a, T>(t: & 'a T) where
T: Debug + 'a {
println!("`print_ref`: t is {:?}", t);
}
fn main() {
let x = 7;
let ref_x = Ref(&x);
print_ref(&ref_x);
print(ref_x);
}
```
2022-11-06 22:02:49 -06:00
1. π
2022-03-30 03:08:16 -06:00
```rust,editable
/* Annotate struct with lifetime:
2022-10-29 05:10:38 -05:00
1. `r` and `s` must have different lifetimes
2022-03-30 03:08:16 -06:00
2. lifetime of `s` is bigger than that of 'r'
*/
struct DoubleRef< T > {
r: & T,
s: & T
}
fn main() {
println!("Success!")
}
```
2022-11-06 22:02:49 -06:00
2. ππ
2022-03-30 03:08:16 -06:00
```rust,editable
/* Adding trait bounds to make it work */
struct ImportantExcerpt< 'a> {
part: & 'a str,
}
impl< 'a, 'b> ImportantExcerpt< 'a> {
fn announce_and_return_part(& 'a self, announcement: & 'b str) -> & 'b str {
println!("Attention please: {}", announcement);
self.part
}
}
fn main() {
println!("Success!")
}
```
2022-11-06 22:02:49 -06:00
3. ππ
2022-03-30 03:08:16 -06:00
```rust,editable
/* Adding trait bounds to make it work */
fn f< 'a, 'b>(x: & 'a i32, mut y: & 'b i32) {
y = x;
let r: & 'b & 'a i32 = &&0;
}
fn main() {
println!("Success!")
}
```
## HRTB(Higher-ranked trait bounds)
Type bounds may be higher ranked over lifetimes. These bounds specify a bound is true for all lifetimes. For example, a bound such as `for<'a> &'a T: PartialEq<i32>` would require an implementation like:
```rust
impl< 'a> PartialEq< i32 > for & 'a T {
// ...
}
```
and could then be used to compare a `&'a T` with any lifetime to an `i32` .
2022-10-29 05:10:38 -05:00
Only a higher-ranked bound can be used here, because the lifetime of the reference is shorter than any possible lifetime parameter on the function.
2022-03-30 03:08:16 -06:00
2022-11-06 22:02:49 -06:00
4. πππ
2022-08-08 10:10:59 -05:00
```rust,editable
2022-03-30 03:08:16 -06:00
/* Adding HRTB to make it work!*/
fn call_on_ref_zero< 'a, F>(f: F) where F: Fn(& 'a i32) {
let zero = 0;
f(&zero);
}
fn main() {
2022-10-29 05:10:38 -05:00
println!("Success!");
2022-03-30 03:08:16 -06:00
}
```
## NLL (Non-Lexical Lifetime)
Before explaining NLL, let's see some code first:
```rust
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
let r3 = & mut s;
println!("{}", r3);
}
```
Based on our current knowledge, this code will cause en error due to violating the borrowing rules in Rust.
But if you `cargo run` it, then everything will be ok, so what's going on here?
The ability of the compiler to tell that a reference is no longer used at a point before the end of the scope, is called **Non-Lexical Lifetimes** (**NLL** for short).
With this ability the compiler knows when is the last time that a reference is used and optimizing the borrowing rules based on this knowledge.
```rust
let mut u = 0i32;
let mut v = 1i32;
let mut w = 2i32;
// lifetime of `a` = Ξ± βͺ Ξ² βͺ Ξ³
let mut a = & mut u; // --+ Ξ± . lifetime of `&mut u` --+ lexical "lifetime" of `&mut u` ,`& mut u`, `&mut w` and `a`
use(a); // | |
*a = 3; // < ----------------- + |
... // |
a = & mut v; // --+ Ξ². lifetime of `&mut v` |
use(a); // | |
*a = 4; // < ----------------- + |
... // |
a = & mut w; // --+ Ξ³ . lifetime of `&mut w` |
use(a); // | |
*a = 5; // < ----------------- + < -------------------------- +
```
## Reborrow
After learning NLL, we can easily understand reborrow now.
**Example**
```rust
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl Point {
fn move_to(& mut self, x: i32, y: i32) {
self.x = x;
self.y = y;
}
}
fn main() {
let mut p = Point { x: 0, y: 0 };
let r = & mut p;
// Here comes the reborrow
let rr: & Point = &*r;
println!("{:?}", rr); // Reborrow ends here, NLL introduced
// Reborrow is over, we can continue using `r` now
r.move_to(10, 10);
println!("{:?}", r);
}
```
2022-11-06 22:02:49 -06:00
5. ππ
2022-03-30 03:08:16 -06:00
```rust,editable
/* Make it work by reordering some code */
fn main() {
let mut data = 10;
let ref1 = & mut data;
let ref2 = & mut *ref1;
*ref1 += 1;
*ref2 += 2;
println!("{}", data);
}
```
## Unbound lifetime
See more info in [Nomicon - Unbounded Lifetimes ](https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html ).
## More elision rules
```rust
impl< 'a> Reader for BufReader< 'a> {
// 'a is not used in the following methods
}
2022-10-29 05:10:38 -05:00
// can be written as :
2022-03-30 03:08:16 -06:00
impl Reader for BufReader< '_> {
}
```
```rust
// Rust 2015
struct Ref< 'a, T: 'a> {
field: & 'a T
}
// Rust 2018
struct Ref< 'a, T> {
field: & 'a T
}
```
## A difficult exercise
2022-11-06 22:02:49 -06:00
6. ππππ
2022-08-08 10:10:59 -05:00
```rust,editable
2022-03-30 03:08:16 -06:00
/* Make it work */
struct Interface< 'a> {
manager: & 'a mut Manager< 'a>
}
impl< 'a> Interface< 'a> {
pub fn noop(self) {
println!("interface consumed");
}
}
struct Manager< 'a> {
text: & 'a str
}
struct List< 'a> {
manager: Manager< 'a>,
}
impl< 'a> List< 'a> {
pub fn get_interface(& 'a mut self) -> Interface {
Interface {
manager: & mut self.manager
}
}
}
fn main() {
let mut list = List {
manager: Manager {
text: "hello"
}
};
list.get_interface().noop();
println!("Interface should be dropped here and the borrow released");
use_list(&list);
}
fn use_list(list: & List) {
println!("{}", list.manager.text);
}
2022-08-08 10:10:59 -05:00
```
2023-01-20 06:01:54 -06:00
> You can find the solutions [here](https://github.com/sunface/rust-by-practice/blob/master/solutions/lifetime/advance.md)(under the solutions path), but only use it when you need it :)