From ccfe1e578f096073bc1c8723fe852ceb432845d0 Mon Sep 17 00:00:00 2001 From: Dhghomon <56599343+Dhghomon@users.noreply.github.com> Date: Tue, 21 Jul 2020 15:00:36 +0900 Subject: [PATCH] More on lifetimes --- README.md | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/README.md b/README.md index 7df12f8..7b043fb 100644 --- a/README.md +++ b/README.md @@ -3860,6 +3860,162 @@ So now ```fn returns_str() -> &'static str``` tells Rust: "don't worry, we will But ```'static``` is not the only lifetime. Actually, every variable has a lifetime, but usually we don't have to write it. We only have to write the lifetime when the compiler doesn't know. +Here is an example of another lifetime. Imagine we want to create a ```City``` struct and give it a ```&str``` for the name. Later we will have many more ```&str```s because we need faster performance than with ```String```. So we write it like this, but it won't work: + +```rust +#[derive(Debug)] +struct City { + name: &str, + date_founded: u32, +} + +fn main() { + let my_city = City { + name: "Ichinomiya", + date_founded: 1921, + }; +} +``` + +The compiler says: + +``` +error[E0106]: missing lifetime specifier + --> src\main.rs:3:11 + | +3 | name: &str, + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +2 | struct City<'a> { +3 | name: &'a str, + | +``` + +Rust needs a lifetime for ```&str``` because ```&str``` is a reference. What happens when the value that ```name``` points to is dropped? That would be unsafe. + +What about ```'static```, will that work? We used it before. Let's try: + +```rust +#[derive(Debug)] +struct City { + name: &'static str, // change &str to &'static str + date_founded: u32, +} + +fn main() { + + let my_city = City { + name: "Ichinomiya", + date_founded: 1921, + }; + + println!("{} was founded in {}", my_city.name, my_city.date_founded); + +} +``` + +Okay, that works. And maybe this is what you wanted for the struct. However, note that we can only take "string literals", so not references to something else. So this will not work: + +```rust +#[derive(Debug)] +struct City { + name: &'static str, // must live for the whole program + date_founded: u32, +} + +fn main() { + + let city_names = vec!["Ichinomiya".to_string(), "Kurume".to_string()]; // city_names does not live for the whole program + + let my_city = City { + name: &city_names[0], // This is a &str, but not a &'static str. It is a reference to a value inside city_names + date_founded: 1921, + }; + + println!("{} was founded in {}", my_city.name, my_city.date_founded); + +} +``` + +The compiler says: + +``` +error[E0597]: `city_names` does not live long enough + --> src\main.rs:12:16 + | +12 | name: &city_names[0], + | ^^^^^^^^^^ + | | + | borrowed value does not live long enough + | requires that `city_names` is borrowed for `'static` +... +18 | } + | - `city_names` dropped here while still borrowed +``` + +So now we will try what the compiler suggested before. It said to try writing ```struct City<'a>``` and ```name: &'a str```. This means that it will only take a reference for ```name``` if it lives as long as ```City```. + +```rust +#[derive(Debug)] +struct City<'a> { // City has lifetime 'a + name: &'a str, // and name also has lifetime 'a. + date_founded: u32, +} + +fn main() { + + let city_names = vec!["Ichinomiya".to_string(), "Kurume".to_string()]; + + let my_city = City { + name: &city_names[0], + date_founded: 1921, + }; + + println!("{} was founded in {}", my_city.name, my_city.date_founded); + +} +``` + +Also remember that you can write anything instead of ```'a``` if you want: + +```rust +#[derive(Debug)] +struct City<'city> { // The lifetime is now called 'city + name: &'city str, // and name has the 'city lifetime + date_founded: u32, +} +``` + +So usually you will write ```'a, 'b, 'c``` etc. because it is quick and the usual way to write. But you can always change it if you want. + +Also remember this important fact: ```'a``` etc. don't change the actual lifetime of variables. They are like traits for generics. Remember when we wrote generics? For example: + +```rust +fn prints(input: T) { + println!("T is {}", input); +} +``` + +When you write ```T: Display```, it means "please only take T if it has Display". +It does not mean: "I am giving Display to T". + +The same is true for lifetimes. When you write 'a here: + +```rust +#[derive(Debug)] +struct City<'a> { + name: &'a str, + date_founded: u32, +} +``` + +It means "please only take an input for ```name``` if it lives at least as long as ```City```". +It does not mean: "I will make the input for ```name``` live as long as ```City```". + + + # Interior mutability ## Cell