0

You can run the code below here.

use std::rc::Rc;
use std::cell::RefCell;
#[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 add_child(&mut self, t: Rc<RefCell<Turtle>>) {
        &self.children.push(t);
    }
    pub fn breed(mut t1: Turtle, mut t2: Turtle, name: String) -> Turtle{
        let t = Turtle {name: name, children:Vec::new()};
        let s = Rc::new(RefCell::new(Turtle {name: name, children:Vec::new()}));
        t1.add_child(Rc::clone(&s));
        t2.add_child(Rc::clone(&s));
        t
    }
}

Before I added the name field, it was running without any issues, but now it's not. Here's the error:

error[E0382]: use of moved value: `name`
  --> src/lib.rs:18:52
   |
16 |     pub fn breed(mut t1: Turtle, mut t2: Turtle, name: String) -> Turtle{
   |                                                  ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
17 |         let t = Turtle {name: name, children:Vec::new()};
   |                               ---- value moved here
18 |         let s = Rc::new(RefCell::new(Turtle {name: name, children:Vec::new()}));
   |                                                    ^^^^ value used here after move

I was unable to fix the error while satisfying these conditions:

  1. add_child now takes in a Turtle, what I had originally, instead of a Rc<RefCell<Turtle>>

  2. breed still has 2 turtle or &turtle parameter, and calls add_child to add to t1 and t2

  3. There is no longer a need to call the Turtle constructor with tons of new variables twice.

  4. The Rc RefCell paradigm is still employed.

Condition 3 is important because I have left out a bunch of fields from Turtle, and if I add them back in and keep the declarations of s and t, there will be lots of repeated code. So the question stands, how do I replace s and t with one grand unified variable that can be passed around to add_child and used for the return value of the breed function without causing any move errors? Condition 1 is not so important to me, and if add_child continues to require a Rc RefCell, so be it. Condition 4 is very important because it means the rest of my code, once I add it back in, will run smoothly. Note that the parameters of breed can be changed to &turtle if needed, although I imagine this won't be necessary.

Update: I decided to make the function return a &Turtle, but it still doesn't work.

pub fn breed<'a>(t1: &'a mut Turtle, t2: &'a mut Turtle, name: String) -> &'a 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.borrow_mut()
}

Error:

error[E0515]: cannot return value referencing local variable `s`
  --> src/lib.rs:20:9
   |
20 |         &*s.borrow_mut()
   |         ^^-^^^^^^^^^^^^^
   |         | |
   |         | `s` is borrowed here
   |         returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing temporary value
  --> src/lib.rs:20:9
   |
20 |         &*s.borrow_mut()
   |         ^^--------------
   |         | |
   |         | temporary value created here
   |         returns a value referencing data owned by the current function

What do I do now? I've exhausted all my options for what could be returned by the function, knowing it must be a &Turtle and it must point to the same turtle that s does. In other words, if t1 or t2 modifies a child, the same modification must apply to the turtle that was returned.

Update 2: The reason I need to return a Turtle or &Turtle is due to the fact I'm using the result in a different class as shown in the function below.

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 t = Turtle::breed(&mut self.turtles[t1_index].t.borrow_mut(),&mut self.turtles[t2_index].t.borrow_mut(),child_name);
    self.add_turtle(*t);
}
Display name
  • 267
  • 1
  • 8
  • As the error says, you are trying to move the String. You can clone it instead. You could also make it a `&str` which is just a reference so can be copied without allocation overhead, but that will introduce lifetime complexity. – Peter Hall May 09 '21 at 01:07
  • In the end, you have two turtles; one that you are returning and another owned by the `Rc`. They can't both own the same string. – Peter Hall May 09 '21 at 01:10
  • @PeterHall Check my update. I'm fine with them owning the same string because I'm fine with the returned turtle being modified whenever t1 or t2 modifies its children. In fact, this is exactly what I want to happen. – Display name May 10 '21 at 03:24
  • " I'm fine with them owning the same string because I'm fine with the returned turtle being modified whenever t1 or t2 modifies its children." Maybe you're fine with that; but Rust as a language is specifically designed to NOT be fine with that. ;) – Lagerbaer May 10 '21 at 03:27
  • Can't you just return `s` in your function? Not borrowing, but actually returning? And style-wise that's a bit iffy anyway; it's generally cleaner to have a function _either_ return something _or_ do something side-effect wise. In your case, it's adding a child to the input turtles _and_ returning that child turtle. Maybe you don't actually need to return it? – Lagerbaer May 10 '21 at 03:31
  • @Lagerbaer I need the result for another function breed_turtles, which calls yet another function add_turtle, which must take a Turtle (no exceptions), see the 2nd update. – Display name May 10 '21 at 03:40
  • @Lagerbaer If I use your idea of returning s, I would need to replace the last line of breed_turtles with self.add_turtle(*t.borrow_mut()), which results in the error "move occurs because value has type `Turtle`, which does not implement the `Copy` trait" – Display name May 10 '21 at 03:46
  • I mean yeah that's how ownership works: If your code _must_ take a `Turtle` instead of a `RefCell` or whatever, then it _must_ take ownership, which then means _nobody else_ may own it. – Lagerbaer May 10 '21 at 03:51
  • @Lagerbaer I know, but in this case it seems Rust has an issue with add_turtle taking ownership, and I would like it to take ownership because there's no other way to implement breed_turtle short of rewriting the code in add_turtle, which is only making my style worse and delaying the inevitable as add_turtle also has a bug I need to fix. In fact, it is a vital requirement that add_turtle transfers ownership. – Display name May 10 '21 at 03:54

1 Answers1

0

I followed Lagerbear's suggestion and made breed return s, made an add_turtle_ref function which takes a TurtleRef = {t: Rc<RefCell>} to use in breed_turtles instead, and finally implemented add_turtle with add_turtle_ref. However, add_turtle_ref has an error and the saga continues here.

Display name
  • 267
  • 1
  • 8