Clone method not implemented in scope when using Reference-Counting (Rc) Pointer


#1

I have written some source code (see below code snippet) based on the Rust Standard Library’s example of the std::rc Module (which is “stable”) to learn how to use Reference-Counting (Pointers) in Rust. However, when I try to run it I get the following compile errors (shown below after the source code). I am using rustc 1.0.0-nightly (270a677d4 2015-03-07) (built 2015-03-07).

So far to try and overcome the error I have tried the following courses of action without any luck:

  • Tried importing the following use std::clone::Clone; and use std::clone;, but the compile errors remained the same
  • Tried importing the following instead use core::clone::Clone;, which results in error ```error: unresolved import core::clone::Clone. Maybe a missing extern crate core?````
  • Tried prefixing with &'static mut (i.e. <&'static mut Planet>) but the compile errors remained the same
  • Tried downloading the latest version of Rust Nightly and Cargo, but did not make any difference

Here is my source code (now edited with comments removed):

use std::old_io;
use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;

fn main() {
    try_reference_counted_boxes();
}

fn try_reference_counted_boxes() {

    struct Galaxy {
        name: String,
        planets: RefCell<Vec<Weak<Planet>>>
    }

    struct Planet {
        id: i32,
        owner: Rc<Galaxy>
    }

    struct Star {
        id: i32,
        owner: Rc<Galaxy>
    }

    let my_galaxy1 : Rc<Galaxy> = Rc::new(
        Galaxy { 
            name: "Milky Way".to_string(),
            planets: RefCell::new(Vec::new())
        }
    );

    let earth = Planet { id: 1, owner: my_galaxy1.clone() };
    let mars = Planet { id: 2, owner: my_galaxy1.clone() };

    my_galaxy1.planets.borrow_mut().push(earth.clone().downgrade());
    my_galaxy1.planets.borrow_mut().push(mars.clone().downgrade());

    for planet_opt in my_galaxy1.planets.borrow().iter() {
        let planet = planet_opt.upgrade().unwrap();
        println!("Planet {} owned by {}", planet.id, planet.owner.name);
    }

    drop(my_galaxy1);

    println!("Earth {} owned by {}", earth.id, earth.owner.name);
    println!("Mars {} owned by {}", mars.id, mars.owner.name);

}

Here are the compiler errors:

src/main.rs:743:48: 743:55 error: type `try_reference_counted_boxes::Planet` does not implement any method in scope named `clone`
src/main.rs:743     my_galaxy1.planets.borrow_mut().push(earth.clone().downgrade());
                                                               ^~~~~~~
src/main.rs:743:55: 743:55 help: methods from traits can only be called if the trait is implemented and in scope; the following trait defines a method `clone`, perhaps you need to implement it:
src/main.rs:743:55: 743:55 help: candidate #1: `core::clone::Clone`
src/main.rs:744:47: 744:54 error: type `try_reference_counted_boxes::Planet` does not implement any method in scope named `clone`
src/main.rs:744     my_galaxy1.planets.borrow_mut().push(mars.clone().downgrade());
                                                              ^~~~~~~
src/main.rs:744:54: 744:54 help: methods from traits can only be called if the trait is implemented and in scope; the following trait defines a method `clone`, perhaps you need to implement it:
src/main.rs:744:54: 744:54 help: candidate #1: `core::clone::Clone`
error: aborting due to 2 previous errors

TWiR quote of the week
#2

earth and mars are Planets, they are not Rcs. The Planet type either needs to implement Clone or needs to be wrapped in an Rc. Probably the latter, since you wish to call downgrade.


#3

Thanks very much @huon. I read through a book chapter on Ownership whilst reflecting upon your comments and now it’s now successfully compiling. I have included a version of the fixed code below.

I did not need to explicitly import Clone with use std::clone::Clone; at all. All it entailed was recognising that I had declared earth and mars as Planets (Box type by default), and that I needed to explicitly wrap them inside an Rc to instead make it of Rc type, which provides access to the clone() method. Wrapping a binding with a Reference-Count (Owning Handle) Pointer Type (i.e. Rc), as I understand it, will cause Rust to perform bookkeeping by means of incrementing a counter to track each allocation we make to heap memory to store a new instance of a resource (i.e. a new instance of my_galaxy1_owner, earth_owner, or mars_owner), and uses this counter to ensure they are all deallocated at the end of their useful scope to prevent any dangling pointers.

Is there anything wrong with nesting the structs (Data Structures) that I am using within the function where I am using them? Or is it Rust best practice to move them outside of all the functions?

Also, is it necessary or considered Rust best practice to explicitly drop any of the bindings or Rc’s (i.e. drop(my_galaxy1_owner);) before the end of the try_reference_counted_boxes function? Or could I rely on Rust to perform all necessary deallocation when they go out of scope at the end of the try_reference_counted_boxes function to avoid any memory leakage?

use std::old_io;
use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;

fn main() {
    try_reference_counted_boxes();
}

fn try_reference_counted_boxes() {

    struct Galaxy {
        name: String,
        planets: RefCell<Vec<Weak<Planet>>>,
    }

    struct Planet {
        id: i32,
        owner: Rc<Galaxy>,
    }

    struct Star {
        id: i32,
        owner: Rc<Galaxy>,
    }

    let my_galaxy1 = Galaxy { 
        name: "Milky Way".to_string(),
        planets: RefCell::new(Vec::new()),
    };

    let my_galaxy1_owner : Rc<Galaxy> = Rc::new(my_galaxy1);

    let earth = Planet { id: 1, owner: my_galaxy1_owner.clone() };
    let mars = Planet { id: 2, owner: my_galaxy1_owner.clone() };

    let earth_owner : Rc<Planet> = Rc::new(earth);
    let mars_owner : Rc<Planet> = Rc::new(mars);

    my_galaxy1_owner.planets.borrow_mut().push(earth_owner.clone().downgrade());
    my_galaxy1_owner.planets.borrow_mut().push(mars_owner.clone().downgrade());

    for planet_opt in my_galaxy1_owner.planets.borrow().iter() {
        let planet = planet_opt.upgrade().unwrap();
        println!("Planet {} owned by {}", planet.id, planet.owner.name);
    }

    drop(my_galaxy1_owner);

    println!("Earth {} owned by {}", earth_owner.id, earth_owner.owner.name);
    println!("Mars {} owned by {}", mars_owner.id, mars_owner.owner.name);

}

#4

There’s nothing particularly wrong, but I often find it distracts from the main body of the function, making it harder to understand. (Depends on the precise circumstances and is personal preference, of course.)

You don’t have to call drop. In fact, I’m not sure one can ever change a "direct"1 memory leak to a non-leak by calling drop. In fact, drop is nothing special, it is defined like:

fn drop<T>(_: T) {}

All drop does is take its argument by value, and immediately let it go out of scope in the function body. Destructors in Rust are created by implementations of the Drop trait. The drop method of that trait is implicitly called when values go out of scope. Rc implements Drop, and the destructor manages freeing the memory appropriately.

In summary: you don’t need to call drop in this case, the destructor of my_galaxy1_owner will ensure memory is cleaned up.

1I’m making up terminology: by “direct”, I mean ignoring things like leaking memory by causing some threads to deadlock. In other words, a “direct” leak is when the program exits the same with/without the drop, except for the lack of destructor running/memory being freed.