borrowed value does not live long enough reference in a struct - rust

I am new to Rust and stumbled upon a problem that I cannot wrap my head around. I have a struct Customer that contains a vector of account references. The compiler suggested I should use the lifetime specifier 'a which as far as I understand means, that the accounts cannot be destroyed before the customer.
pub struct Account {
pub balance: f32,
}
pub struct Customer<'a> {
accounts: Vec<&'a mut Account>,
}
Now I want to implement a function that creates a new account for my customer, I assumed it could work something like this:
impl<'a> Customer<'a> {
pub fn create_account(&mut self, balance: f32) {
let mut account = Account{balance};
self.accounts.push(&mut account);
}
}
However I get an error:
error[E0597]: `account` does not live long enough
--> src\banking\mod.rs:16:28
|
8 | impl<'a> Customer<'a> {
| -- lifetime `'a` defined here
...
16 | self.accounts.push(&mut account);
| -------------------^^^^^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `account` is borrowed for `'a`
17 | }
| - `account` dropped here while still borrowed
I assume the reason is that the account variable gets destroyed at the end of the create_account function. However, how can I create a new account and assign it to the customer?

Your customers currently mutably borrow their accounts. Someone has to own that data. Since there's no other structures in the code that I can see, I assume you meant to have the customers own the data. If so, just get rid of the &mut.
pub struct Customer {
accounts: Vec<Account>,
}
...
impl<'a> Customer<'a> {
pub fn create_account(&mut self, balance: f32) {
let account = Account { balance };
self.accounts.push(account);
}
}
As a bonus point, Customer no longer has lifetime arguments, it simply exists independently.

You are correctly identifying the cause of the compiler error. For how to fix it, you need to consider your design. Because your Customer type has a vector of references to Accounts, structurally you are implying that the accounts are owned by something else. For example, you could have a Bank type that actually stores and owns the Accounts, and then the Customer can have a reference. Alternatively, you could change your Customer type to own the Accounts, rather than references to Accounts.

Related

Why do I get lifetime error when using a reference in a struct that is still valid?

I'm new to Rust, and I'm trying to learn more about lifetimes. I came across the following piece of code, which won't compile (Playground):
struct Person<'a, 'b> {
name: &'a str,
email: &'b str,
}
fn main() {
let my_name: String = String::from("John Doe");
let me: Person;
{
let my_email: String = String::from("john#example.com");
me = Person {
name: &my_name,
email: &my_email,
};
}
println!("My name is {}.", me.name);
}
Notice the use of two different lifetime parameters in the definition of Person.
Here is the compilation error:
error[E0597]: `my_email` does not live long enough
--> src/main.rs:15:20
|
15 | email: &my_email,
| ^^^^^^^^^ borrowed value does not live long enough
16 | };
17 | }
| - `my_email` dropped here while still borrowed
18 |
19 | println!("My name is {}.", me.name);
| ------- borrow later used here
The compiler complains about my_email not living long enough. However, I'm not using it, or any reference to it (me.email, for example). I'm rather trying to print me.name, which refers to my_name, and the lifetime of it won't end until the end of main function.
However, the following piece of code, which is a small modification of the code above, compiles and runs as intended (Playground):
struct Person<'a, 'b> {
name: &'a str,
email: &'b str,
}
fn main() {
let my_name: String = String::from("John Doe");
let name_ref: &str;
let me: Person;
{
let my_email: String = String::from("john#example.com");
me = Person {
name: &my_name,
email: &my_email,
};
name_ref = me.name;
}
println!("My name is {}.", name_ref); // Prints "My name is John Doe."
}
I can't understand why the first piece of code doesn't work, despite everything looking fine. Can someone explain what's going on?
You are using it. You are storing it in the struct.
As long as me exists, the me.email exists as well, regardless of whether you are accessing it or not. And according to Rust's soundness rules, a reference that exists has to always be outlived by the data it points to. A reference cannot be dangling.
The reason the second code works is because you do not access the me structure outside of the innermost context. name_ref no longer depends on my_email, and me no longer exists. So Rust is able to destroy me first and can then safely destroy my_email.
One subtle detail to understand here is that name_ref does not depend on me. Assigning a reference to a new variable (in this case, from me.name to name_ref) copies the reference. name_ref purely depends on my_name.
How does the compiler know that? By implicit lifetimes. The Rust compiler actually keeps a hidden lifetime annotation with every reference (at compile time only). So the Rust compiler understands that name_ref actually references my_name (or at least in a more abstract way that the lifetime of the thing name_ref points to has to match my_name).
structs in Rust take over the lifetime of their members. So if you store a reference inside of the struct, the entire struct will depend on the referenced object's lifetime. You could say the struct derives its lifetime dependencies from its members. That's also what the compiler warning tells you: because you use me in general, this lifetime dependency has to be honored. It doesn't matter if you use the actual object.
You can see this already in its declaration:
struct Person<'a, 'b> {...}
The lifetime 'b, in this case, captures the dependency to the my_email object, and the lifetime is attached to the entire Person object, not just its email member.
This can go even further:
struct Person<'a> {
email: Option<&'a str>,
}
fn main() {
let mut me: Person;
{
let my_email: String = String::from("john#example.com");
me = Person {
email: Some(&my_email),
};
me.email = None;
}
println!("My name is {:?}.", me.email);
}
error[E0597]: `my_email` does not live long enough
--> src/main.rs:11:25
|
11 | email: Some(&my_email),
| ^^^^^^^^^ borrowed value does not live long enough
...
15 | }
| - `my_email` dropped here while still borrowed
16 |
17 | println!("My name is {:?}.", me.email);
| -------- borrow later used here
Note that at the point where the inner context is left, me.email contains None, so the me object isn't even referencing my_email any more. Nonetheless, creating the object with Person { email: Some(&my_email) } burns this lifetime into the instantiated type of me. A lifetime behaves similar to a generic: it is burned into the type of the variable, and the type of the variable cannot change by mutating it.

Why rust told me that a reference still borrowed at the end of main function?

As you can see in the following code, I have two traits, one is called Hittable, and the other is called Material (I have been studying the book "ray-tracing-in-one-weekend", but use Rust).
The Hittable trait implements hit function for some objects (just like Sphere in this code), and every kind of objects includes its material (just like Glass, Wood...).
In my real project, the Sphere struct and another struct (called HitRecord in this book, used as mut reference to pass result in hit function), they both include &dyn Material, so that I need add lifetime parameter for both of them. However, to accomplish that, I should add lifetime parameter in the trait declaration, so I can assign the same lifetime parameter for Sphere and hit.
But the compiler indicates that the reference still under borrowed when the main function ends, I have no idea for that...
trait Hittable<'a> {
fn hit(&self);
}
trait Material {
fn result(&self);
}
struct Glass;
impl Material for Glass {
fn result(&self) {
println!("Glass is broken!");
}
}
struct Sphere<'a> {
name: String,
mat_ptr: &'a dyn Material,
}
impl<'a> Hittable<'a> for Sphere<'a> {
fn hit(&self) {
println!("Name is {}", self.name);
self.mat_ptr.result();
}
}
struct HT<'a> {
pub objects: Vec<Box<dyn Hittable<'a>>>,
}
fn main() {
let mut list = HT { objects: vec![] };
let surface_material = Glass;
let s = Sphere {
name: String::from("球"),
mat_ptr: &surface_material,
};
list.objects.push(Box::new(s));
}
the message shows
Compiling rust_test v0.1.0 (/home/hnyls2002/rust_test)
error[E0597]: `surface_material` does not live long enough
--> src/main.rs:38:18
|
38 | mat_ptr: &surface_material,
| ^^^^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `surface_material` is borrowed for `'static`
...
41 | }
| - `surface_material` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
This is because dyn Hittable<'a> is actually dyn Hittable<'a> + 'static, and thus s is required to live for 'static. The fix is to change HT to:
struct HT<'a> {
pub objects: Vec<Box<dyn Hittable<'a> + 'a>>,
}
Then you'll get a long but pretty self-explanatory error:
error[E0597]: `surface_material` does not live long enough
--> src/main.rs:38:18
|
38 | mat_ptr: &surface_material,
| ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
41 | }
| -
| |
| `surface_material` dropped here while still borrowed
| borrow might be used here, when `list` is dropped and runs the destructor for type `HT<'_>`
|
= note: values in a scope are dropped in the opposite order they are defined
Because surface_material is defined after list, it'll be dropped before and when the potential destructor for dyn Hittable will run it may access the freed surface_material. The fix is just to swap the declaration order of list and surface_material:
fn main() {
// always define before the container you are pushing into
let surface_material = Glass;
let mut list = HT { objects: vec![] };
// ...
}

"X does not live long enough" error when overriding struct field (that has the same lifetime as other fields) inside nested scope

I implemented a struct where I gave all reference fields the same lifetime. It seems like lifetimes don't work when I override field inside inner scope. I get an error:
error[E0597]: str2 does not live long enough
This is my code:
struct Test<'a> {
a: Option<&'a String>,
b: Option<&'a String>,
}
impl<'a> Test<'a> {
pub fn new(a: Option<&'a String>) -> Self {
Self {
a,
b: None,
}
}
}
fn main () {
let str1 = String::from("test1");
let mut test = Test::new(Some(&str1));
{
let str2 = String::from("test2");
test.b = Some(&str2);
}
println!("{:?} and {:?}", test.a, test.b);
}
This is minimal sandbox implementation.
Could you explain how to force references to work with a defined lifetime? And why doesn't the code compile in this case?
I think you misunderstand.
References with lifetime annotations don't keep values alive, they just specify how long they must be alive in order to be storable in the struct.
References do not take ownership of the variable. Lifetimes just ensure that whoever owns the variable does not destroy it before 'a is over.
The compiler automatically figures out what 'a must be. In your case, the latest point where test.a and test.b are getting used is the println!(...). Therefore the compiler determines that 'a of the test object must be a lifetime that is at least until the println!(...).
Then, the compiler realizes that the owner of test.b, which is str2, gets dropped at the end of the }, which does not match the lifetime 'a of the test object, as it doesn't reach the println!(...).
If you look at the more detailed error message of cargo, by running either cargo check, cargo build or cargo run, you can see just that:
error[E0597]: `str2` does not live long enough
--> src/main.rs:17:23
|
17 | test.b = Some(&str2);
| ^^^^^ borrowed value does not live long enough
18 | }
| - `str2` dropped here while still borrowed
19 |
20 | println!("{:?} and {:?}", test.a, test.b);
| ------ borrow later used here

Can I move from &mut self to &self within the same function?

I'm trying to learn a bit of Rust through a toy application, which involves a tree data structure that is filled dynamically by querying an external source. In the beginning, only the root node is present. The tree structure provides a method get_children(id) that returns a [u32] of the IDs of all the node's children — either this data is already known, or the external source is queried and all the nodes are inserted into the tree.
I'm running into the following problem with the borrow checker that I can't seem to figure out:
struct Node {
id: u32,
value: u64, // in my use case, this type is much larger and should not be copied
children: Option<Vec<u32>>,
}
struct Tree {
nodes: std::collections::HashMap<u32, Node>,
}
impl Tree {
fn get_children(&mut self, id: u32) -> Option<&[u32]> {
// This will perform external queries and add new nodes to the tree
None
}
fn first_even_child(&mut self, id: u32) -> Option<u32> {
let children = self.get_children(id)?;
let result = children.iter().find(|&id| self.nodes.get(id).unwrap().value % 2 == 0)?;
Some(*result)
}
}
Which results in:
error[E0502]: cannot borrow `self.nodes` as immutable because it is also borrowed as mutable
--> src/lib.rs:19:43
|
18 | let children = self.get_children(id)?;
| ---- mutable borrow occurs here
19 | let result = children.iter().find(|&id| self.nodes.get(id).unwrap().value % 2 == 0)?;
| ---- ^^^^^ ---------- second borrow occurs due to use of `self.nodes` in closure
| | |
| | immutable borrow occurs here
| mutable borrow later used by call
Since get_children might insert nodes into the tree, we need a &mut self reference. However, the way I see it, after the value of children is known, self no longer needs to be borrowed mutably. Why does this not work, and how would I fix it?
EDIT -- my workaround
After Chayim Friedman's answer, I decided against returning Self. I mostly ran into the above problem when first calling get_children to get a list of IDs and then using nodes.get() to obtain the corresponding Node. Instead, I refactored to provide the following functions:
impl Tree {
fn load_children(&mut self, id: u32) {
// If not present yet, perform queries to add children to the tree
}
fn iter_children(&self, id: u32) -> Option<IterChildren> {
// Provides an iterator over the children of node `id`
}
}
Downgrading a mutable reference into a shared reference produces a reference that should be kept unique. This is necessary for e.g. Cell::from_mut(), which has the following signature:
pub fn from_mut(t: &mut T) -> &Cell<T>
This method relies on the uniqueness guarantee of &mut T to ensure no references to T are kept directly, only via Cell. If downgrading the reference would mean the unqiueness could have been violated, this method would be unsound, because the value inside the Cell could have been changed by another shared references (via interior mutability).
For more about this see Common Rust Lifetime Misconceptions: downgrading mut refs to shared refs is safe.
To solve this you need to get both shared references from the same shared reference that was created from the mutable reference. You can, for example, also return &Self from get_children():
fn get_children(&mut self, id: u32) -> Option<(&Self, &[u32])> {
// This will perform external queries and add new nodes to the tree
Some((self, &[]))
}
fn first_even_child(&mut self, id: u32) -> Option<u32> {
let (this, children) = self.get_children(id)?;
let result = children.iter().find(|&id| this.nodes.get(id).unwrap().value % 2 == 0)?;
Some(*result)
}

Rust, how to return reference to something in a struct that lasts as long as the struct?

I am porting a compiler I wrote to Rust. In it, I have an enum Entity which represents things like functions and variables:
pub enum Entity<'a> {
Variable(VariableEntity),
Function(FunctionEntity<'a>)
// Room for more later.
}
I then have a struct Scope which is responsible for holding on to these entities in a hash map, where the key is the name given by the programmer to the entity. (For example, declaring a function named sin would put an Entity into the hash map at the key sin.)
pub struct Scope<'a> {
symbols: HashMap<String, Entity<'a>>,
parent: Option<&'a Scope<'a>>
}
I would like to be able to get read-only references to the objects in the HashMap so that I can refer to it from other data structures. For example, when I parse a function call, I want to be able to store a reference to the function that is being called instead of just storing the name of the function and having to look up the reference every time I need the actual Entity object corresponding to the name. To do so, I have made this method:
impl<'a> Scope<'a> {
pub fn lookup(&self, symbol: &str) -> Option<&'a Entity<'a>> {
let result = self.symbols.get(symbol);
match result {
Option::None => match self.parent {
Option::None => Option::None,
Option::Some(parent) => parent.lookup(symbol),
},
Option::Some(_value) => result
}
}
}
However, this results in a compilation error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/vague/scope.rs:29:31
|
29 | let result = self.symbols.get(symbol);
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 28:3...
--> src/vague/scope.rs:28:3
|
28 | / pub fn lookup(&self, symbol: &str) -> Option<&'a Entity<'a>> {
29 | | let result = self.symbols.get(symbol);
30 | | match result {
31 | | Option::None => match self.parent {
... |
36 | | }
37 | | }
| |___^
note: ...so that reference does not outlive borrowed content
--> src/vague/scope.rs:29:18
|
29 | let result = self.symbols.get(symbol);
| ^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 9:6...
--> src/vague/scope.rs:9:6
|
9 | impl<'a> Scope<'a> {
| ^^
= note: ...so that the expression is assignable:
expected std::option::Option<&'a vague::entity::Entity<'a>>
found std::option::Option<&vague::entity::Entity<'_>>
Things I Tried
There are several ways to make the compilation error go away, but none of them give the behavior I want. First, I can do this:
pub fn lookup(&self, symbol: &str) -> Option<&Entity<'a>> {
But this means the reference will not live long enough, so I can't put it into a struct or any other kind of storage that will outlive the scope that lookup is called from. Another solution was this:
pub fn lookup(&self, symbol: &str) -> Option<&'a Entity> {
Which I do not understand why it could compile. As part of the struct definition, things inside Entity objects in the hash map must live at least as long as the scope, so how can the compiler allow the return type to be missing that? Additionally, why would the addition of <'a> result in the previous compiler error, since the only place the function is getting Entitys from is from the hash map, which is defined as having a value type of Entity<'a>. Another bad fix I found was:
pub fn lookup(&'a self, symbol: &str) -> Option<&'a Entity<'a>> {
Which would mean that lookup can only be called once, which is obviously a problem. My previous understanding was incorrect, but the problem still remains that requiring the reference to self to have the same lifetime as the whole object severely restricts the code in that I can't call this method from a reference with any shorter lifetime, e.g. one passed in as a function argument or one created in a loop.
How can I go about fixing this? Is there some way I can fix the function as I have it now, or do I need to implement the behavior I'm looking for in an entirely different way?
Here's the signature you want:
pub fn lookup(&self, symbol: &str) -> Option<&'a Entity<'a>>
Here's why it can't work: it returns a reference that borrows an Entity for longer than lookup initially borrowed the Scope. This isn't illegal, but it means that the reference lookup returns can't be derived from the self reference. Why? Because given the above signature, this is valid code:
let sc = Scope { ... };
let foo = sc.lookup("foo");
drop(sc);
do_something_with(foo);
This code compiles because it has to: there is no lifetime constraint that the compiler could use to prove it wrong, because the lifetime of foo isn't coupled to the borrow of sc. But clearly, if lookup were implemented the way you first tried, foo would contain a dangling pointer after drop(sc), which is why the compiler rejected it.
You must redesign your data structures to make the given signature for lookup work. It's not clear how best to do this given the code in the question, but here are some ideas:
Decouple the lifetimes in Scope so that the parent is borrowed for a different lifetime than the symbols. Then have lookup take &'parent self. This probably will not work by itself, depending on what you need to do with the Entitys, but you may need to do it anyway if you need to distinguish between the lifetimes of different data.
pub struct Scope<'parent, 'sym> {
symbols: HashMap<String, Entity<'sym>>,
parent: Option<&'parent Scope<'parent, 'sym>>,
}
impl<'parent, 'sym> Scope<'parent, 'sym> {
pub fn lookup(&'parent self, symbol: &str) -> Option<&'parent Entity<'sym>> {
/* ... */
}
}
Store your Scopes and/or your Entitys in an arena. An arena can give out references that outlive the self-borrow, as long as they don't outlive the arena data structure itself. The tradeoff is that nothing in the arena will be deallocated until the whole arena is destroyed. It's not a substitute for garbage collection.
Use Rc or Arc to store your Scopes and/or your Entitys and/or whatever data Entity stores that contains references. This is one way to get rid of the lifetime parameter completely, but it comes with a small runtime cost.

Resources