first liftime

  1. fn main() {
  2. let a = String::from("aaaaa"); // a goes into scope
  3. {
  4. let b = String::from("bbb"); // b goes into scope
  5. let res = longest(&a, &b);
  6. println!("{}", res);
  7. } // b goes out of scope
  8. }// a goes out of scope
  9. fn longest(x: &str, y: &str) -> &str {
  10. if x.len() > y.len() {
  11. x
  12. } else {
  13. y
  14. }
  15. }

比较哪个变量长,这个代码是无法编译通过的

image.png

编译错误也很清楚,告诉我们,要声明一个lifetime,因为返回值引用了入参,所以编译器不知道返回值的生命周期是什么

  1. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  2. if x.len() > y.len() {
  3. x
  4. } else {
  5. y
  6. }
  7. }

什么一个 ‘a 作为lifetime parameter, 这告诉编译器,longest的返回值的声明周期和入参一样,这里 ‘a 并不是代表,x, y的生命周期是一致的,而是说 返回值 str,x, y 只要在 ‘a 这个scope中就可以,例如,例子里面 a 的生命周期要比 b,res都要长,但他们有一个交集,就是b和res的lifetime

假如这里声明2个lifetime

  1. fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
  2. if x.len() > y.len() {
  3. x
  4. } else {
  5. y
  6. }
  7. }

编译就会报错,但是可以告诉编译器,’a 一定 比 ‘b 活的久

  1. fn longest<'a:'b, 'b>(x: &'a str, y: &'b str) -> &'b str {
  2. if x.len() > y.len() {
  3. x
  4. } else {
  5. y
  6. }
  7. }

其实这里 ‘a, ‘b 并不是和参数x,y 绑定的,只是定义了2个lifetime,告诉编译器,不管x,y 的生命周期如何,返回值的生命周期一定在那个短命的scope里,这里我觉得是

struct

  1. #[derive(Debug)]
  2. struct Config<'a> {
  3. query: &'a String,
  4. filename: String,
  5. case_sensitive: bool,
  6. }
  7. impl <'a> Config<'a> {
  8. fn new(query: &'a String) -> Result<Self, &'static str> {
  9. if query.len() <= 0 {
  10. return Err("invalid argument");
  11. }
  12. Ok(Self{query, filename: String::from("test"), case_sensitive: false})
  13. }
  14. }

假如struct中有个变量是引用,那也需要申明一个生命周期, 这告诉编译器,Config实例的生命周期,和传入query属性的生命周期一致

  1. fn main() {
  2. let q = String::from("query"); // q comes into scope
  3. let cfg = Config::new(&q).unwrap_or_else(|err| { // cfg comes into scope
  4. eprintln!("failed to create config {}", err);
  5. process::exit(1)
  6. });
  7. println!("{:?}", cfg);
  8. println!("{}", cfg.get_query());
  9. } // cfg, q goes out of scope

how to break it?

  1. fn main() {
  2. let q = String::from("query"); // q comes into scope
  3. move_string(q); // q is dead after move_string, so this will not compile
  4. let cfg = Config::new(&q).unwrap_or_else(|err| { // cfg comes into scope
  5. eprintln!("failed to create config {}", err);
  6. process::exit(1)
  7. });
  8. println!("{:?}", cfg);
  9. println!("{}", cfg.get_query());
  10. } // cfg, q goes out of scope
  11. #[derive(Debug)]
  12. struct Config<'a> {
  13. query: &'a String,
  14. filename: String,
  15. case_sensitive: bool,
  16. }
  17. fn move_string(s: String){
  18. }

lifetime Bounds

当struct包含泛型参数的时候,T有可能是另外一个struct,这个struct也可能包含其他的引用,例如

  1. struct Ref<'a, T>(&'a T);

Because T can be any type, T could be a reference or a type that holds one or more references, each of which could have their own lifetimes. Rust can’t be sure T will live as long as 'a.


  1. struct Ref<'a, T: 'a>(&'a T);

Adding lifetime bounds on T to specify that any references in T live at least as long as 'a

elision rule

  • The first rule is that each parameter that is a reference gets its own lifetime parameter.
  • The second rule is if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters: fn foo<'a>(x: &'a i32) -> &'a i32.
  • The third rule is if there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, the lifetime of self is assigned to all output lifetime parameters. This third rule makes methods much nicer to read and write because fewer symbols are necessary.