Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you combine lifetimes in rust?

Tags:

rust

This code:

struct Foo<'a> {
  value: Option<&'a int>,
  parent: Option<&'a Foo<'a>>
}

impl<'a> Foo<'a> {
  fn bar<'a, 'b, 'c: 'a + 'b>(&'a self, other:&'b int) -> Foo<'c> {
    return Foo { value: Some(other), parent: Some(self) };
  }
}

fn main() {
  let e = 100i;
  {
    let f = Foo { value: None, parent: None };
    let g:Foo;
    {
       g = f.bar(&e);
    }
    // <--- g should be valid here
  }
  // 'a of f is now expired, so g should not be valid here.

  let f2 = Foo { value: None, parent: None };
  {
    let e2 = 100i;
    let g:Foo;
    {
       g = f2.bar(&e2);
    }
    // <--- g should be valid here
  }
  // 'b of e2 is now expired, so g should not be valid here.
}

Fails to compile with the error:

<anon>:8:30: 8:35 error: cannot infer an appropriate lifetime due to conflicting requirements
<anon>:8     return Foo { value: Some(other), parent: Some(self) };
                                      ^~~~~
<anon>:7:3: 9:4 note: consider using an explicit lifetime parameter as shown: fn bar<'a, 'b>(&'a self, other: &'b int) -> Foo<'b>
<anon>:7   fn bar<'a, 'b, 'c: 'a + 'b>(&'a self, other:&'b int) -> Foo<'c> {
<anon>:8     return Foo { value: Some(other), parent: Some(self) };
<anon>:9   }

(playpen: http://is.gd/vAvNFi )

This is obviously a contrived example, but it is something I want to do occasionally.

So...

1) How do you combine lifetimes? (ie. Return a Foo which has a lifetime of at least 'a or 'b, which ever is shorter)

2) Is there some way to write tests to asset lifetime compile failures? (eg. try to compile a #[test] that uses the function in the wrong way and fails with a lifetime error)

like image 434
Doug Avatar asked Sep 05 '14 01:09

Doug


People also ask

How do lifetimes work in Rust?

Lifetimes are what the Rust compiler uses to keep track of how long references are valid for. Checking references is one of the borrow checker's main responsibilities. Lifetimes help the borrow checker ensure that you never have invalid references.

What is static lifetime Rust?

Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Static items may be placed in read-only memory if the type is not interior mutable. Static items do not call drop at the end of the program.

What is <' A in Rust?

The <> is used to declare lifetimes. This says that bar has one lifetime, 'a. Rust has two main types of strings: &str and String . The &str are called 'string slices' . A string slice has a fixed size, and cannot be mutated.

What is a named lifetime parameter in Rust?

Rust uses lifetime parameters to avoid such potential run-time errors. Since the compiler doesn't know in advance whether the if or the else block will execute, the code above won't compile and an error message will be printed that says, “a lifetime parameter is expected in compare 's signature.”


2 Answers

The bounds 'c: 'a + 'b means that 'c is at least as long as 'a and as long as 'b. However, in this case, the Foo value is valid for exactly the shortest of 'a and 'b: as soon as the data behind either reference goes out of scope the whole Foo must be invalidated. (This is saying that data that is valid for 'c is valid in the union of 'a and 'b.)

In more concrete terms, say 'b = 'static, then 'c: 'a + 'static means that 'c must also be 'static, so the return value would be Foo<'static>. This is clearly not right as it would be "upgrading" the limited 'a self reference into one that lasts forever.

The correct behaviour here is taking the intersection of the lifetimes: the Foo is only valid while both function parameters are valid. The intersection operation is just labelling the references with the same name:

fn bar<'a>(&'a self, other: &'a int) -> Foo<'a>
like image 51
huon Avatar answered Sep 21 '22 08:09

huon


Remove all the limetime parameters on bar and use the 'a lifetime parameter from the impl instead.

impl<'a> Foo<'a> {
  fn bar(&'a self, other:&'a int) -> Foo<'a> {                  // '
    return Foo { value: Some(other), parent: Some(self) };
  }
}

'a will be inferred by the compiler to be the smallest lifetime in which all references are valid.

like image 33
Francis Gagné Avatar answered Sep 18 '22 08:09

Francis Gagné