patternrustMajor
What is the correct way to use lifetimes with a struct in Rust?
Viewed 0 times
withstructusecorrectthelifetimeswayrustwhat
Problem
I want to write this structure:
The
--> src/main.rs:17:24
|
17 | b: B { c: &c },
| ^ borrowed value does not live long enough
18 | }
19 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'b as defined on the method body at 13:5...
--> src/main.rs:13:5
|
13 | fn new() -> A {
| ^^^^^^^^^^^^^^^^^^^^^
error[E0382]: use of moved value:
--> src/main.rs:17:24
|
16 | c: c,
| - value moved here
17 | b: B { c: &c },
| ^ value used here after move
|
= note: move occurs because
`
I've read the Rust documentation on ownership, but I still don't know how to fix it.
struct A {
b: B,
c: C,
}
struct B {
c: &C,
}
struct C;The
B.c should be borrowed from A.c.A ->
b: B ->
c: &C -- borrow from --+
|
c: C
This is what I tried:
struct C;
struct B {
c: &'b C,
}
struct A {
b: B,
c: C,
}
impl A {
fn new() -> A {
let c = C;
A {
c: c,
b: B { c: &c },
}
}
}
fn main() {}
But it fails:
error[E0597]: c does not live long enough--> src/main.rs:17:24
|
17 | b: B { c: &c },
| ^ borrowed value does not live long enough
18 | }
19 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'b as defined on the method body at 13:5...
--> src/main.rs:13:5
|
13 | fn new() -> A {
| ^^^^^^^^^^^^^^^^^^^^^
error[E0382]: use of moved value:
c--> src/main.rs:17:24
|
16 | c: c,
| - value moved here
17 | b: B { c: &c },
| ^ value used here after move
|
= note: move occurs because
c has type C, which does not implement the Copy trait`
I've read the Rust documentation on ownership, but I still don't know how to fix it.
Solution
There is actually more than one reason why the code above fails. Let's break it down a little and explore a few options on how to fix it.
First let's remove the
this fails with:
what it says is that if you assign
The compiler hints at a possible solution in the error message when it says that type
We can make
Now the code above works. Note that what @matthieu-m comments is still true: we can't store both the reference to a value and the value itself in B (we're storing a reference to a value and a COPY of the value here). That's not just for structs, though, it's how ownership works.
Now, if you don't want to (or can't) make
All good then? Not really... we still want to move the creation of
as anticipated, here's our lifetime error:
this is because
One possible solution is to create
playground
First let's remove the
new and try building an instance of A directly in main, so that you see that the first part of the problem has nothing to do with lifetimes:struct C;
struct B {
c: &'b C,
}
struct A {
b: B,
c: C,
}
fn main() {
// I copied your new directly here
// and renamed c1 so we know what "c"
// the errors refer to
let c1 = C;
let _ = A {
c: c1,
b: B { c: &c1 },
};
}this fails with:
error[E0382]: use of moved value: c1
--> src/main.rs:20:20
|
19 | c: c1,
| -- value moved here
20 | b: B { c: &c1 },
| ^^ value used here after move
|
= note: move occurs because c1 has type C, which does not implement the Copy trait
what it says is that if you assign
c1 to c, you move its ownership to c (i.e. you can't access it any longer through c1, only through c). This means that all the references to c1 would be no longer valid. But you have a &c1 still in scope (in B), so the compiler can't let you compile this code.The compiler hints at a possible solution in the error message when it says that type
C is non-copyable. If you could make a copy of a C, your code would then be valid, because assigning c1 to c would create a new copy of the value instead of moving ownership of the original copy.We can make
C copyable by changing its definition like this:#[derive(Copy, Clone)]
struct C;Now the code above works. Note that what @matthieu-m comments is still true: we can't store both the reference to a value and the value itself in B (we're storing a reference to a value and a COPY of the value here). That's not just for structs, though, it's how ownership works.
Now, if you don't want to (or can't) make
C copyable, you can store references in both A and B instead.struct C;
struct B {
c: &'b C,
}
struct A {
b: B,
c: &'a C, // now this is a reference too
}
fn main() {
let c1 = C;
let _ = A {
c: &c1,
b: B { c: &c1 },
};
}All good then? Not really... we still want to move the creation of
A back into a new method. And THAT's where we will run in trouble with lifetimes. Let's move the creation of A back into a method:impl A {
fn new() -> A {
let c1 = C;
A {
c: &c1,
b: B { c: &c1 },
}
}
}as anticipated, here's our lifetime error:
error[E0597]: c1 does not live long enough
--> src/main.rs:17:17
|
17 | c: &c1,
| ^^ borrowed value does not live long enough
...
20 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 13:1...
--> src/main.rs:13:1
|
13 | impl A {
| ^^^^^^^^^^^^^^
error[E0597]: c1 does not live long enough
--> src/main.rs:18:24
|
18 | b: B { c: &c1 },
| ^^ borrowed value does not live long enough
19 | }
20 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 13:1...
--> src/main.rs:13:1
|
13 | impl A {
| ^^^^^^^^^^^^^^
this is because
c1 is destroyed at the end of the new method, so we can't return a reference to it.fn new() -> A {
let c1 = C; // we create c1 here
A {
c: &c1, // ...take a reference to it
b: B { c: &c1 }, // ...and another
}
} // and destroy c1 here (so we can't return A with a reference to c1)One possible solution is to create
C outside of new and pass it in as a parameter:struct C;
struct B {
c: &'b C,
}
struct A {
b: B,
c: &'a C
}
fn main() {
let c1 = C;
let _ = A::new(&c1);
}
impl A {
fn new(c: &'a C) -> A {
A {c: c, b: B{c: c}}
}
}playground
Code Snippets
struct C;
struct B<'b> {
c: &'b C,
}
struct A<'a> {
b: B<'a>,
c: C,
}
fn main() {
// I copied your new directly here
// and renamed c1 so we know what "c"
// the errors refer to
let c1 = C;
let _ = A {
c: c1,
b: B { c: &c1 },
};
}#[derive(Copy, Clone)]
struct C;struct C;
struct B<'b> {
c: &'b C,
}
struct A<'a> {
b: B<'a>,
c: &'a C, // now this is a reference too
}
fn main() {
let c1 = C;
let _ = A {
c: &c1,
b: B { c: &c1 },
};
}impl<'a> A<'a> {
fn new() -> A<'a> {
let c1 = C;
A {
c: &c1,
b: B { c: &c1 },
}
}
}fn new() -> A<'a> {
let c1 = C; // we create c1 here
A {
c: &c1, // ...take a reference to it
b: B { c: &c1 }, // ...and another
}
} // and destroy c1 here (so we can't return A with a reference to c1)Context
Stack Overflow Q#27589054, score: 96
Revisions (0)
No revisions yet.