rust-by-practice/en/src/lifetime/advance.md

287 lines
6.3 KiB
Markdown
Raw Normal View History

2022-10-29 05:10:38 -05:00
# Advance lifetime
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 :)