1

After a long battle with ownership, lifetime, move, and borrow errors, I'm down to an error in add_turtle_ref. The desired behavior is that add_turtle_ref adds the input tref to self.turtles, a Vec<TurtleRef>, and self.map, a HashMap<&str, Vec<TurtleRef>>.

Playground.

use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

#[derive(Clone)]
pub struct TurtleRef {
    t: Rc<RefCell<Turtle>>,
}
impl TurtleRef {
    pub fn new(r: Turtle) -> TurtleRef {
        TurtleRef {
            t: Rc::new(RefCell::new(r)),
        }
    }
}

pub struct Campus<'a> {
    turtles: Vec<TurtleRef>,
    map: HashMap<&'a str, Vec<TurtleRef>>,
}

impl Campus<'_> {
    pub fn new() -> Campus<'static> {
        Campus {
            turtles: Vec::new(),
            map: HashMap::new(),
        }
    }
    pub fn size(&self) -> usize {
        self.turtles.len()
    }
    pub fn add_turtle(&mut self, turtle: Turtle) {
        self.add_turtle_ref(TurtleRef::new(turtle));
    }

    pub fn add_turtle_ref(&mut self, tref: TurtleRef) {
        self.turtles.push(tref.clone());
        let n = tref.t.borrow().name();
        self.map.entry(n).or_insert(Vec::new()).push(tref.clone());
    }
    pub fn breed_turtles(&mut self, t1_index: usize, t2_index: usize, child_name: String) {
        let n = self.size();
        if t1_index > n - 1 || t2_index > n - 1 {
            panic!("out of bounds");
        }
        let tref = Turtle::breed(
            &mut self.turtles[t1_index].t.borrow_mut(),
            &mut self.turtles[t2_index].t.borrow_mut(),
            child_name,
        );
        self.add_turtle_ref(TurtleRef { t: tref });
    }
}

#[derive(Debug)]
pub struct Turtle {
    name: String,
    children: Vec<Rc<RefCell<Turtle>>>,
}

impl Turtle {
    pub fn new(name: String) -> Turtle {
        Turtle {
            name: name,
            children: Vec::new(),
        }
    }
    pub fn name(&self) -> &String {
        &self.name
    }
    pub fn add_child(&mut self, t: Rc<RefCell<Turtle>>) {
        self.children.push(t);
    }
    pub fn breed(t1: &mut Turtle, t2: &mut Turtle, name: String) -> Rc<RefCell<Turtle>> {
        let s = Rc::new(RefCell::new(Turtle {
            name: name,
            children: Vec::new(),
        }));
        t1.add_child(Rc::clone(&s));
        t2.add_child(Rc::clone(&s));
        s
    }
}

The error:

error[E0597]: `tref.t` does not live long enough
  --> src/lib.rs:38:17
   |
36 |     pub fn add_turtle_ref(&mut self, tref: TurtleRef) {
   |                           --------- has type `&mut Campus<'1>`
37 |         self.turtles.push(tref.clone());
38 |         let n = tref.t.borrow().name();
   |                 ^^^^^^ borrowed value does not live long enough
39 |         self.map.entry(n).or_insert(Vec::new()).push(tref.clone());
   |         ----------------- argument requires that `tref.t` is borrowed for `'1`
40 |     }
   |     - `tref.t` dropped here while still borrowed

error[E0716]: temporary value dropped while borrowed
  --> src/lib.rs:38:17
   |
36 |     pub fn add_turtle_ref(&mut self, tref: TurtleRef) {
   |                           --------- has type `&mut Campus<'1>`
37 |         self.turtles.push(tref.clone());
38 |         let n = tref.t.borrow().name();
   |                 ^^^^^^^^^^^^^^^       - temporary value is freed at the end of this statement
   |                 |
   |                 creates a temporary which is freed while still in use
39 |         self.map.entry(n).or_insert(Vec::new()).push(tref.clone());
   |         ----------------- argument requires that borrow lasts for `'1`

I've tried all sorts of clever tricks such as obtaining the name, dereferencing it to a normal string, then turning the String back into a &String, but this just causes the Rust compiler to complain even faster: you can't dereference a string!

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
Display name
  • 267
  • 1
  • 8
  • I don't understand your code (because it's missing large parts), but `self.map.entry(tref.t.borrow_mut()....)` looks wrong because `entry` requires an owned key. – user4815162342 May 10 '21 at 07:11
  • Maybe try to get your work to work with clone(), and then when it works try to remove the clones and deal with references and RCs... – JP Moresmau May 10 '21 at 10:43
  • @user4815162342 I know there's an issue with borrowing or ownership, but how do I get an owned key then? – Display name May 10 '21 at 17:53
  • @JPMoresmau Check my update. Using clone won't help me get a &String without an error. All I want to do is just get the &String so I can use it for the hashmap. – Display name May 10 '21 at 17:54
  • 1
    Your core question is answered by [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132/155423) Your problem is solved by [How to use a struct's member as its own key when inserting the struct into a map without duplicating it?](https://stackoverflow.com/q/41035869/155423) – Shepmaster May 10 '21 at 18:03
  • 1
    [The duplicates applied](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=31641b500cda03ed46be6f63488f2cc2) – Shepmaster May 10 '21 at 18:05
  • 1
    I've [provided exact code](https://stackoverflow.com/questions/67464708/adding-the-same-object-to-a-hashmap-and-vector-without-violating-the-rules-of-bo?noredirect=1#comment119263848_67464708) that allows the code to compile -- how is it not possible to understand how it solves the problem? What's missing? – Shepmaster May 10 '21 at 18:37
  • 2
    @Shepmaster Sorry, that link was too close to my playground link and I accidentally closed it because I forgot clicking on it and thought I had opened the same tab twice. This solved my problem. I had a HashMap before and the compiler didn't like it because the size was unknown, now I know that HashMap, on the other hand is perfectly fine, and will remember that difference between String and str for the future. – Display name May 10 '21 at 18:42

0 Answers0