Lifetimes

Lifetimes tell the compiler how to check:

whether references live long enough to be valid in any given situation.

For example lifetimes say “make sure parameter ‘a’ lives as long as parameter ‘b’ so that the return value is valid”.

They are only necessary on borrows, i.e. references

  • Since copied parameters or moves are owned in their scope and cannot be referenced outside.
  • Lifetimes mean that calling code of e.g. functions can be checked to make sure their arguments are valid.
  • Lifetimes are restrictive of their callers.

Further information

Rustlings

returns a value referencing data owned by the current function

expected named lifetime parameter

function

// lifetimes1.rs
//
// The Rust compiler needs to know how to check whether supplied references are
// valid, so that it can let the programmer know if a reference is at risk
// of going out of scope before it is used. Remember, references are borrows
// and do not own their own data. What if their owner goes out of scope?
//
// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is '{}'", result);
}

Hint

Let the compiler guide you. Also take a look at the book if you need help:

struct

// lifetimes3.rs
//
// Lifetimes are also needed when structs hold references.
//
// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

struct Book {
    author: &str,
    title: &str,
}

fn main() {
    let name = String::from("Jill Smith");
    let title = String::from("Fish Flying");
    let book = Book { author: &name, title: &title };

    println!("{} by {}", book.title, book.author);
}

Hint

If you use a lifetime annotation in a struct’s fields, where else does it need to be added?

borrowed value does not live long enough

lifetimes2

// lifetimes2.rs
//
// So if the compiler is just validating the references passed
// to the annotated parameters and the return type, what do
// we need to change?
//
// Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is '{}'", result);
}

Hint: 将块看作生命周期隐式声明

Remember that the generic lifetime ’a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y. You can take at least two paths to achieve the desired result while keeping the inner block:

  1. Move the string2 declaration to make it live as long as string1 (how is result declared?)
  2. Move println! into the inner block

Q&A

Q: 为什么不能改成

#![allow(unused)]
fn main() {
fn longest<'a:'b, 'b>(x: &'a str, y: &'b str) -> &'b str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
}

A: 因为可能返回’a x, 也可能返回’b y. 这里主要问题在于返回的引用有两种生命周期。

lifetimes4

extern crate regex; // just for running in rust playground
use regex::Regex;

fn get_publish_date(title: String) -> String{
    let date_re = Regex::new(r"(\d{4}-\d{2}-\d{2})").unwrap();
    // let publish_date = date_re.captures(title.as_str()).unwrap().get(1).unwrap().as_str();
    // ----------------------------------------------------^^^^^^ here to match.
    let publish_date = match date_re.captures(title.as_str()) {
        Some(captured) => captured.get(1).unwrap().as_str(), // 这里unwrap()之后只有as_str()方法, 没有to_string()
        // None => format!("Unable to extract date from {}", title).as_str() // temporary value is freed at the end of this statement
        // None => "Unable to extract date from {title}" // str is equal to &'static str ?
        None => {
            let temp = format!("Unable to extract date from {}", title);
            temp.as_str()
        }
    };
    publish_date.to_string()
}

fn main() {
    let title = "【Rust Daily】2023-01-21"; // &str
    let publish_date = get_publish_date(title);
    println!("Title: {}\nPublish_data: {}\n", title, publish_date);
}

Hint: 函数返回引用才会考虑live long enough

extern crate regex; // just for running in rust playground
use regex::Regex;

fn get_publish_date(title: &str) -> String{
    let date_re = Regex::new(r"(\d{4}-\d{2}-\d{2})").unwrap();
    // let publish_date = date_re.captures(title.as_str()).unwrap().get(1).unwrap().as_str();
    // ----------------------------------------------------^^^^^^ here to match.
    let publish_date = match date_re.captures(title) {
        Some(captured) => captured.get(1).unwrap().as_str().to_string(), // 这里unwrap()之后只有as_str()方法, 没有to_string()
        // None => format!("Unable to extract date from {}", title).as_str() // temporary value is freed at the end of this statement
        // None => "Unable to extract date from {title}" // str is equal to &'static str ?
        None => format!("Unable to extract date from {}", title)
    };
    publish_date
}

fn main() {
    let title = "【Rust Daily】2023-01-21";
    let publish_date = get_publish_date(title);
    println!("Title: {}\nPublish_data: {}\n", title, publish_date);
}