Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Referencing a containing struct in Rust (and calling methods on it)

Editor's note: This code example is from a version of Rust prior to 1.0 and is not syntactically valid Rust 1.0 code. Updated versions of this code produce different errors, but the answers still contain valuable information.

I'm trying to write a container structure in Rust where its elements also store a reference to the containing container so that they can call methods on it. As far as I could figure out, I need to do this via Rc<RefCell<T>>. Is this correct?

So far, I have something like the following:

struct Container {
  elems: ~[~Element]
}

impl Container {
  pub fn poke(&mut self) {
    println!("Got poked.");
  }
}

struct Element {
  datum: int,
  container: Weak<RefCell<Container>>
}

impl Element {
  pub fn poke_container(&mut self) {
    let c1 = self.container.upgrade().unwrap(); // Option<Rc>
    let mut c2 = c1.borrow().borrow_mut(); // &RefCell
    c2.get().poke();
//    self.container.upgrade().unwrap().borrow().borrow_mut().get().poke();
//    -> Error: Borrowed value does not live long enough * 2
  }
}

fn main() {
  let container = Rc::new(RefCell::new(Container{ elems: ~[] }));
  let mut elem1 = Element{ datum: 1, container: container.downgrade() };
  let mut elem2 = Element{ datum: 2, container: container.downgrade() };
  elem1.poke_container();
}

I feel like I am missing something here. Is accessing the contents of a Rc<RefCell<T>> really this difficult (in poke_container)? Or am I approaching the problem the wrong way?

Lastly, and assuming the approach is correct, how would I write an add method for Container so that it could fill in the container field in Element (assuming I changed the field to be of type Option<Rc<RefCell<T>>>? I can't create another Rc from &mut self as far as I know.

like image 809
Kolja Avatar asked Jan 27 '14 19:01

Kolja


1 Answers

The long chain of method calls actually works for me on master without any changes, because the lifetime of "r-values" (e.g. the result of function calls) have changed so that the temporary return values last until the end of the statement, rather than the end of the next method call (which seemed to be how the old rule worked).

As Vladimir hints, overloadable dereference will likely reduce it to

self.container.upgrade().unwrap().borrow_mut().poke();

which is nicer.

In any case, "mutating" shared ownership is always going to be (slightly) harder to write in Rust that either single ownership code or immutable shared ownership code, because it's very easy for such code to be memory unsafe (and memory safety is the core goal of Rust).

like image 190
huon Avatar answered Nov 02 '22 23:11

huon