Borrowed value des not live long enough

This code work

#[test]
    fn should_get_a_next_transition() {
        //let mut node2 = Node::new("node2".to_string());
        let mut node3 = Node::new("node3".to_string());
        let mut node1 = Node::new("node1".to_string());

        //node1.add_next(&mut node2, 85);
        node1.add_next(&mut node3, 12);

        // call
        let transition = node1.get_next_mut("node3");

        // assertions
        let tr = transition.unwrap();
        assert_eq!("node3", tr.node.get_id());
        assert_eq!(12, tr.weight);
    }

But this one does not work

#[test]
    fn should_get_a_next_transition() {
        let mut node2 = Node::new("node2".to_string());
        let mut node3 = Node::new("node3".to_string());
        let mut node1 = Node::new("node1".to_string());

        node1.add_next(&mut node2, 85);
        node1.add_next(&mut node3, 12);

        // call
        let transition = node1.get_next_mut("node3");

        // assertions
        let tr = transition.unwrap();
        assert_eq!("node3", tr.node.get_id());
        assert_eq!(12, tr.weight);
    }

Error message :

error[E0597]: `node3` does not live long enough                          ] 0/3: shortest-way, shortest-way
   --> src\graph.rs:225:29
    |
225 |         node1.add_next(&mut node3, 12);
    |                             ^^^^^ borrowed value does not live long enough
...
234 |     }
    |     - `node3` dropped here while still borrowed
    |
    = note: values in a scope are dropped in the opposite order they are created

warning: variable does not need to be mutable
   --> src\graph.rs:221:13
    |
221 |         let mut node3 = Node::new("node3".to_string());
    |             ----^^^^^
    |             |
    |             help: remove this `mut`
    |
    = note: #[warn(unused_mut)] on by default

error: aborting due to previous error

please advise.

Here is a playaground to that gather all the code that have the issue

You can’t fix it with references in a scalable way. The reason is that you are tying node2's and node3's lifetimes together by putting them in node1, but that conflicts with their definition at the let bindings, which say they have different lifetimes. This is happening because inside Node<'a> you have HashMap<String, Transition<'a>>, because of this Rust thinks that everything Transition must have the exact same lifetime. This then leads Rust to think that every mutable reference to Node<'a> inside each of the transitions the HashMap of transitions must also have the same lifetime. So in the case of you’re last test, this means that node2 and node3 must have the exact same lifetime.
In this specific case you could fix it by doing something like let (mut node2, mut node3) = (Node::new("node2".to_string()), Node::new("node3".to_string()));, which says that both node2, and node3 have the same-lifetime, but this doesn’t scale at all.
Instead you can use you could use std::rc::Rc or std::sync::Arc to handle references to Node. This way you don’t have to worry about lifetimes, and you can do self-referential data structures. But do note that since both Rc and Arc are both shared type, you will need interior mutability to regain mutability. You can get this with std::cell::RefCell, std::sync::Mutex, or std::sync::RwLock.

Now I just threw a lot of information out there, so to help you decide which to use. You only need stuff from the std::sync module if you are doing any muti-threading, so you could stay away from that on you’re first go at it, and use std::rc::Rc with std::cell::RefCell. Reason being it is harder to debug a deadlock resulting from a Mutex or RwLock than a panic from a RefCell.

note using the methods I listed above will not allow you to break the aliasing xor mutability rules of Rust (seen in the rules around &T and &mut T).


How to use std::rc::Rc with std::cell::RefCell

Type type you would be looking at would be Rc<RefCell<Node>>, and you can create it like let node = Rc::new(RefCell::new(node));. To access nodes immutably you would use node.borrow() or node.try_borrow() (this uses RefCell::borrow and RefCell::try_borrow), and to access nodes mutable you would use RefCell::borrow_mut and RefCell::try_borrow_mut.

The difference between the try and normal variants is that the try_* functions don’t panic and return a Result, which will be an error variant if you try to break the aliasing xor mutability rule. :slight_smile: Good luck!


I just found a reason for why std::rc::Rc is useful, that is much better put that what I did, see this comment here.

1 Like

In structs you should generally use owned types (Rc/Arc/Box/Vec, etc.), not borrowed references, unless you’re absolutely sure you know what you’re doing and can’t use owned types.

Temporary references are not like regular pointers, and they’re not the right tool for building data structures, especially not for linked lists: http://cglab.ca/~abeinges/blah/too-many-lists/book/

2 Likes

Thank you @kornel, I will give a look at it.

Appreciate your continued recommendation on this… This can be one of the tougher lessons to learn at first