In Rust, when we want a struct to contain references, we typically define their lifetimes as such:
struct Foo<'a> {
x: &'a i32,
y: &'a i32,
}
But it's also possible to define multiple lifetimes for different references in the same struct:
struct Foo<'a, 'b> {
x: &'a i32,
y: &'b i32,
}
When is it ever useful to do this? Can someone provide some example code that doesn't compile when both lifetimes are 'a but does compile when the lifetimes are 'a and 'b (or vice versa)?
After staying up way too late, I was able to come up with an example case where the lifetimes matter. Here is the code:
static ZERO: i32 = 0;
struct Foo<'a, 'b> {
x: &'a i32,
y: &'b i32,
}
fn get_x_or_zero_ref<'a, 'b>(x: &'a i32, y: &'b i32) -> &'a i32 {
if *x > *y {
return x
} else {
return &ZERO
}
}
fn main() {
let x = 1;
let v;
{
let y = 2;
let f = Foo { x: &x, y: &y };
v = get_x_or_zero_ref(&f.x, &f.y);
}
println!("{}", *v);
}
If you were to change the definition of Foo to this:
struct Foo<'a> {
x: &'a i32,
y: &'a i32,
}
Then the code won't compile.
Basically, if you want to use the fields of the struct on any function that requires it's parameters to have different lifetimes, then the fields of the struct must have different lifetimes as well.
I want to re-answer my question here since it's still showing up high in search results and I feel I can explain better. Consider this code:
Rust Playground
struct Foo<'a> {
x: &'a i32,
y: &'a i32,
}
fn main() {
let x = 1;
let v;
{
let y = 2;
let f = Foo { x: &x, y: &y };
v = f.x;
}
println!("{}", *v);
}
And the error:
error[E0597]: `y` does not live long enough
--> src/main.rs:11:33
|
11 | let f = Foo { x: &x, y: &y };
| ^^ borrowed value does not live long enough
12 | v = f.x;
13 | }
| - `y` dropped here while still borrowed
14 | println!("{}", *v);
| -- borrow later used here
What's going on here?
The lifetime of f.x has the requirement of being at least large enough to encompass the scope of x up until the println! statement (since it's initialized with &x and then assigned to v).
The definition of Foo specifies that both f.x and f.y use the same generic lifetime 'a, so the lifetime of f.y must be at least as large as f.x.
But, that can't work, because we assign &y to f.y, and y goes out of scope before the println!. Error!
The solution here is to allow Foo to use separate lifetimes for f.x and f.y, which we do using multiple generic lifetime parameters:
Rust Playground
struct Foo<'a, 'b> {
x: &'a i32,
y: &'b i32,
}
Now the lifetimes of f.x and f.y aren't tied together. The compiler will still use a lifetime that's valid until the println! statement for f.x. But there's no longer a requirement that f.y uses the same lifetime, so the compiler is free to choose a smaller lifetime for f.y, such as one that is valid only for the scope of y.
Here is another simple example where the struct definition has to use two lifetimes in order to operate as expected. It does not split the aggregate into fields of different lifetimes, but nests the struct with another struct.
struct X<'a>(&'a i32);
struct Y<'a, 'b>(&'a X<'b>);
fn main() {
let z = 100;
//taking the inner field out of a temporary
let z1 = ((Y(&X(&z))).0).0;
assert!(*z1 == z);
}
The struct Y has two lifetime parameters, one for its contained field &X, and one for X's contained field &z.
In the operation ((Y(&X(&z))).0).0, X(&z) is created as a temporary and is borrowed. Its lifetime is only in the scope of this operation, expiring at the statement end. But since X(&z)'s lifetime is different from the its contained field &z, the operation is fine to return &z, whose value can be accessed later in the function.
If using single lifetime for Y struct. This operation won't work, because the lifetime of &z is the same as its containing struct X(&z), expiring at the statement end; therefore the returned &z is no longer valid to be accessed afterwards.
See code in the playground.
Related
I'm having difficulties understanding lifetime parameters in the following code snippet.
struct C {
data: Vec<u32>,
cols: usize
}
trait M<'s> {
fn get(&'s self, r: usize, c: usize) -> u32;
fn get_mut(&'s mut self, r: usize, c: usize) -> &'s mut u32;
}
impl<'s> M<'s> for C {
fn get(&'s self, r: usize, c: usize) -> u32 {
return self.data[self.cols*r+c];
}
fn get_mut(&'s mut self, r: usize, c: usize) -> &'s mut u32 {
return &mut self.data[self.cols*r+c];
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create() -> C {
let data = vec![0u32,1u32,2u32,3u32,4u32,5u32];
return C{data, cols: 3};
}
fn select<'s, 'r: 's>(data: &'r mut dyn M<'s>) {
let mut _val: u32 = 0;
for r in 0..2 {
for c in 0..3 {
_val += *data.get_mut(r,c);
}
}
}
#[test]
fn test_select() {
let mut data = create();
select(&mut data);
}
}
The code snippet does not compile, because it complains that *data is borrowed multiple times in the function fn select<'s, 'r: 's>(data: &'r mut dyn M<'s>) {} when calling get_mut (once in every loop iteration). Even safeguarding the questionable line with curly braces (and thus creating a new context) does not help. My expectation (in both cases) would be, that the mutable borrow of &mut data should end right after the execution of that line.
On the other hand, when I remove all lifetime parameters, everything works as expected.
Can anyone explain what's the difference between the two versions (with and without explicit lifetimes)?
I've also tried to find information about additional lifetime parameters for traits, in particular specifying their meaning, but I have found none. So I assume, that they are just a declaration of the used labels inside the trait. But if that is so, then I would assume that leaving out the lifetime parameters completely and applying the eliding rules would lead to the same result.
There are two things to consider. The first is when you use a generic lifetime for a function, that lifetime must be larger than the life of the function call simply by construction. And the second is since the lifetime self is tied to the lifetime parameter of the trait, when you call .get_mut(), data is borrowed for the lifetime of 's. Combining those two principles, data is borrowed for longer than the function call so you can't call it again (its already mutably borrowed).
On the other hand, when I remove all lifetime parameters, everything works as expected. Can anyone explain what's the difference between the two versions (with and without explicit lifetimes)?
Without a generic lifetime on M, the methods will behave as if defined as so:
impl M for C {
fn get<'a>(&'a self, r: usize, c: usize) -> u32 {
return self.data[self.cols * r + c];
}
fn get_mut<'a>(&'a mut self, r: usize, c: usize) -> &'a mut u32 {
return &mut self.data[self.cols * r + c];
}
}
Thus there is no lifetime associated with the trait; the lifetimes given and returned from the function are generic only to those method calls. And since the compiler can choose a new lifetime 'a for each call and it will always pick the shorted lifetime to satisfy its usage, you can then call data.get_mut() multiple times without worry. And I'll be honest, having the lifetime on the trait didn't make much sense with the original code; as mentioned, the code works with all lifetime annotations removed: playground.
Prior question that this is not a duplicate of:
cannot borrow `*self` as mutable because it is also borrowed as immutable
This question is not relevant b/c the answer ended up being "the Rust compiler has a bug", which I'm pretty sure is not the case here.
I have the following structs:
struct Foo {
data: Vec<Bar>,
a: usize,
}
struct Bar {
b: usize
}
Inside impl Foo, I have the following methods:
fn example(&mut self, c: usize) {
let baz = &mut self.data[c];
let z = self.calc(c);
baz.b = 42 + z;
}
fn calc(&self, x: usize) -> usize {
self.a * x
}
Of course, the Rust compiler throws an error saying roughly "a mutable borrow occurs when you create baz, then you do an immutable borrow when you call self.calc and lastly you later use the mutable borrow when you assign to baz.a.
However, I'm accessing disjoint fields on the struct because calc never reads from data that is being written to through baz.
Is there a way to inform the Rust compiler of this?
The problem is the signature of Foo::calc, which takes &self as the receiver. This guarantees to calc that there are no mutable references to all of self, including any of its fields; that is, all of Foo is guaranteed to be immutable from the body of calc's point of view. This is not possible with the code as it is, because self.calc(c) requires to immutably borrow self (all of self) while a mutable borrow is still active.
The fact that calc never reads from data that is being written to baz is irrelevant. In Rust, all there is to know about a function or method is exposed in the signature; the compiler never looks "into" a function to figure out if it's a special case or not.
You can avoid the problem by not requiring calc to take &self as a receiver. For instance, by borrowing individual fields before the call (Self::calc(&self.a, c)) or, as in the example below, by not borrowing self at all:
impl Foo {
fn example(&mut self, c: usize) {
let baz = &mut self.data[c];
// This avoids borrowing `self` in its entirety...
let z = Self::calc(self.a, c);
baz.b = 42 + z;
}
fn calc(a: usize, x: usize) -> usize {
a * x
}
}
I ended up doing:
fn example(&mut self, c: usize) {
let baz = &self.data[c];
// some stuff that requires reading `baz`
let z = self.calc(c);
let baz = &mut self.data[c];
baz.b = 42 + z;
}
fn calc(&self, x: usize) -> usize {
self.a * x
}
Because rust doesn't track individual field borrows across method calls - it assumes you've borrowed from the whole struct, it's not possible to borrow a field from the struct, return it, and then pass that into other mutable methods. That sounds contrived but I have this exact issue right now.
struct Foo {
x: i32,
y: i32
}
impl Foo {
pub fn borrow_x(&mut self) -> &mut i32 {
&mut self.x
}
pub fn cross_product(&mut self, other: &mut i32) {
self.y *= *other;
*other *= self.y;
}
}
fn main() {
let mut foo = Foo{x: 2, y: 4};
let x_ref = foo.borrow_x();
foo.cross_product(x_ref);
println!("x={} y={}", foo.x, foo.y);
}
This of course isn't permitted in rust, the error from the compiler is:
error[E0499]: cannot borrow `foo` as mutable more than once at a time
--> src/main.rs:20:5
|
19 | let x_ref = foo.borrow_x();
| --- first mutable borrow occurs here
20 | foo.cross_product(x_ref);
| ^^^ ----- first borrow later used here
| |
| second mutable borrow occurs here
My first reaction is to use unsafe to tell the compiler that it doesn't really borrow from self:
pub fn borrow_x<'a>(&mut self) -> &'a mut i32 {
unsafe {
std::mem::transmute::<&mut i32, &'a mut i32>(&mut self.x)
}
}
It works, but it requires that you don't actually break rust's aliasing rules and reference foo.x elsewhere by accident.
Then I thought maybe I could split the struct so that the borrow and cross_product methods are on different sibling structs. This does work:
struct Bar {
x: X,
y: Y,
}
struct X {
inner: i32
}
impl X {
pub fn borrow_x(&mut self) -> &mut i32 {
&mut self.inner
}
}
struct Y {
inner: i32
}
impl Y {
pub fn cross_product(&mut self, other: &mut i32) {
self.inner *= *other;
*other *= self.inner;
}
}
fn main() {
let mut bar = Bar{x: X{inner: 2}, y: Y{inner: 4}};
let x_ref = bar.x.borrow_x();
bar.y.cross_product(x_ref);
println!("x={} y={}", bar.x.inner, bar.y.inner);
}
I think one could ditch the method all-together and pass references to both fields into cross_product as a free function. That should be equivalent to the last solution. I find that kind of ugly though, especially as the number of fields needed from the struct grow.
Are there other solutions to this problem?
The thing to keep in mind is that rust is working at a per-function level.
pub fn cross_product(&mut self, other: &mut i32) {
self.y *= *other;
*other *= self.y;
}
The mutability rules ensure that self.y and other are not the same thing.
If you're not careful you can run into this kind of bug in C++/C/Java quite easily.
i.e. imagine if instead of
let x_ref = foo.borrow_x();
foo.cross_product(x_ref);
you were doing
let y_ref = foo.borrow_y();
foo.cross_product(y_ref);
What value should foo.y end up with? (Allowing these to refer to the same object can cause certain performance optimisations to not be applicable too)
As you mentioned, you could go with a free function
fn cross_product(a: &mut i32, b: &mut i32) {
a *= b;
b *= a;
}
but I'd consider ditching mutability altogether
fn cross_product(a:i32, b:i32) -> (i32,i32) {
(a*b, a*b*b)
}
ASIDE: If the return value seems odd to you (it did to me initially too) and you expected (a*b, a*b) you need to think a little harder... and I'd say that alone is a good reason to avoid the mutable version.
Then I can use it like this
let (_x,_y) = cross_product(foo.x, foo.y);
foo.x = _x;
foo.y = _y;
This is a bit verbose, but when RFC-2909 lands we can instead write:
(foo.y, foo.x) = cross_product(foo.x, foo.y);
Here is an example
#[derive(Debug)]
struct Point {
x: Vec<i32>,
y: i32,
}
let mut p = Point { x: vec![1], y: 7 };
// borrow out mutable reference p to a and b
let Point { x: a, y: b } = &mut p;
// mutate a
a.push(2);
// how do I get p back?
println!("{:?}", p);
Is there a way to unborrow the reference without creating a new block or abstract into function?
Use Rust 2018.
In Rust 2018, which has NLL, your example is valid.
You can't. The fields x and y are (mutably) borrowed, which means Point is (mutably) borrowed. All this happens in the same scope, as a result Point will remain mutably borrowed until the end of the scope and you can't have a (immutable or mutable) borrow to the data after it is mutably borrowed. Interior Mutability is what you need to have a look at.
Using RefCell<T> :
use std::cell::RefCell;
#[derive(Debug)]
struct Point {
x: RefCell<Vec<i32>>,
y: i32,
}
fn main() {
let p = Point {
x: RefCell::new(vec![1]),
y: 7,
};
(*p.x.borrow_mut()).push(2);
println!("{:?}", p);
}
Edit 1:
Yes, It is possible as per Yusuke NOJIMA's answer. You will need to add the attribute #![feature(nll)] to your code , using rust nightly.
#![feature(nll)]
#[derive(Debug)]
struct Point {
x: Vec<i32>,
y: i32,
}
...
...
For more, please refer the NLL RFC
I am not sure why the following code does not compile.
use std::cmp::Ordering;
struct MyItr<'a> {
cur: &'a i32,
}
impl<'a> Ord for MyItr<'a> {
fn cmp(&self, other: &MyItr) -> Ordering {
self.cur.cmp(&other.cur)
}
}
impl<'a> PartialOrd for MyItr<'a> {
fn partial_cmp(&self, other: &MyItr<'a>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a> PartialEq for MyItr<'a> {
fn eq(&self, other: &MyItr) -> bool {
self.cur == other.cur
}
}
impl<'a> Eq for MyItr<'a> {}
fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32) {
let t = std::cmp::max(t0, t1);
t.cur = i;
}
fn f1() {
let i0 = 1;
let i1 = 2;
let mut z0 = MyItr { cur: &i0 };
let mut z1 = MyItr { cur: &i1 };
let i2 = 3;
f0(&mut z0, &mut z1, &i2);
}
$ cargo build
Compiling foo v0.1.0 (file:///private/tmp/foo)
error: `z1` does not live long enough
--> lib.rs:40:1
|
39 | f0(&mut z0, &mut z1, &i2);
| -- borrow occurs here
40 | }
| ^ `z1` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
My understanding is the borrowed references to z0 and z1 are backed once the f0 invocation ends. However, The compiler seems to assume the borrowed references are not backed.
$ cargo --version
cargo 0.20.0-nightly (41e490480 2017-05-16)
There are two problems here. The first is that you've over-specified lifetimes, creating a situation the compiler just can't deal with.
fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32)
You've told the compiler that all the arguments must be pointers with the same lifetime. The compiler can narrow overlapping lifetimes, but in this case that doesn't help. You've specified that the pointer to the MyItrs has the same lifetime as the thing they point to, and the outer pointers are mutable.
The second problem is that (even after fixing that), what you're trying to do is just outright unsafe and will lead to dangling pointers.
Here's a more minimal example:
struct S<'a> {
ptr: &'a i32,
}
fn f<'b>(t: &'b mut S<'b>, new_ptr: &'b i32) {}
fn main() {
let i0 = 1;
let mut s = S { ptr: &i0 };
let i1 = 2;
f(&mut s, &i1);
}
What is 'b? Well, the compiler can only narrow lifetimes, so usually you'd just take the lifetimes of everything you're trying to pass and pick the shortest one. In this case, that would be the lifetime of i1. So, it has to narrow the lifetime of &s. The lifetime on the pointer to s itself isn't a problem (you can narrow how long you take a borrow for), but narrowing the inner lifetime (the one used for the ptr field) is a problem.
If the compiler narrowed the lifetime of s.ptr, you would then be able to store &i1 in that field. s expects s.ptr to outlive itself, but that will no longer be true: i1 will be destroyed before s is, meaning s.ptr will contain a dangling pointer. And Rust will not allow that to happen.
As a result, Rust can't narrow s's inner 'a lifetime... but if it can't narrow it, then that means 'b must be the full, un-narrowed 'a. But wait, that means that 'b is longer than the lifetime of s itself and i1. And that's impossible.
Hence the failure.
The solution requires two things. First, you need to not over-specify lifetimes. Secondly, you need to ensure that some valid lifetime exists at all; in the case of your original code, that means moving i2 above z0 and z1 so that it outlives them. Like so:
fn f0<'a>(t0: &mut MyItr<'a>, t1: &mut MyItr<'a>, i: &'a i32) {
let t: &mut MyItr<'a> = std::cmp::max(t0, t1);
t.cur = i;
}
fn f1() {
let i0 = 1;
let i1 = 2;
let i2 = 3;
let mut z0 = MyItr { cur: &i0 };
let mut z1 = MyItr { cur: &i1 };
f0(&mut z0, &mut z1, &i2);
}
A rule of thumb: don't just spam a single lifetime everywhere. Only use the same lifetime for things that should be the same.