1

I do not understand the error message I am getting for the following code.

use std::collections::HashMap; use std::rc::Weak; struct ThingPool { thing_map: HashMap<String, Thing>, } impl ThingPool { pub fn get(&self, key: &str) -> Option<&Thing> { self.thing_map.get(key) } } struct Thing { pool: Weak<RefCell<ThingPool>>, key: String, parent_key: String, } impl Thing { pub fn parent(&self) -> Option<&Thing> { let pool = &*self .pool .upgrade() .expect("FATAL: ThingPool no longer exists") .borrow(); pool.get(&self.parent_key) } } fn main() { // ... } 

I have a recursive data structure of Things, and I am trying to write a method that looks up the parent of a particular Thing in the ThingPool based on its key. The error message I am getting is:

 --> src/main.rs:30:9 | 24 | let pool = &*self | ______________________- 25 | | .pool 26 | | .upgrade() 27 | | .expect("FATAL: ThingPool no longer exists") 28 | | .borrow(); | |_____________________- temporary value created here 29 | 30 | pool.get(&self.parent_key) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function 

I understand that I cannot return a reference to a local value, but I do not understand what the local value is in this case. What is the "data owned by the current function"? The value I am returning is a value of the HashMap in the ThingPool. I am not copying the pool and therefore the HashMap and therewith the value should not be local to the function. What am I missing?

1 Answer 1

1

When you call upgrade().expect("..."), that creates a new Rc that is owned by the current function. When you borrow out of the Rc, that reference cannot outlive the Rc you just created: when the Rc is dropped, it will decrement the reference count, potentially deallocating the pool and leaving any references invalid. Currently, it is dropped when you leave the function, but the function still returns a reference to its contents.

Breaking up the chain of calls helps a little bit.

pub fn parent(&self) -> Option<&Thing> { let pool_rc = self .pool .upgrade() .expect("FATAL: ThingPool no longer exists"); let pool_ref = pool_rc.borrow(); let parent_ref = pool_ref.get(&self.parent_key); return parent_ref; } 

pool_rc is the Rc pointer. It doesn't get returned, so it's a local that must be dropped at the end of the function call.

pool_ref is a reference to the pool. It doesn't know anything about the reference count, and it's relying on pool_rc to increment the count before it starts and decrement the count after it ends.

parent_ref is in the same pickle as pool_ref. It doesn't know about the reference count, and is relying on pool_rc stick around until after parent_ref expires so that pool_rc does not decrement the reference count while parent_ref needs access to the pool.

When you return parent_ref you are trying to make it outlive pool_rc, which could cause issues.

Here's an example of what could go wrong if this function was allowed:

  1. There is some external Rc on the pool, so the reference count on the pool is 1.
  2. You run Thing::parent, returning a reference to an item in the pool, but because the Rc to the pool is dropped when the function ends, the reference count on the pool is still 1.
  3. That external Rc is dropped, taking the refcount to 0 and deallocating the pool.
  4. You use the reference returned from parent, which refers to the already-deallocated memory.

You might be able to find some solution where you return a Rc to the pool alongside the reference to the parent, guaranteeing that it exists. However, I would advocate a different strategy: rather than hiding the fact that there is a pool, explicitly embrace it. Moving the parent call into the ThingPool makes the ownership dynamics clear.

use std::collections::HashMap; struct ThingPool { thing_map: HashMap<String, Thing>, } impl ThingPool { pub fn get(&self, key: &str) -> Option<&Thing> { self.thing_map.get(key) } pub fn parent(&self, key: &str) -> Option<&Thing> { self.get(key).and_then(|thing| self.get(&thing.parent_key)) } } struct Thing { key: String, parent_key: String, } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.