6

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Kolja
  • 1,197
  • 9
  • 17
  • 2
    BTW, there is absolutely no need to use `~[~Element]` instead of `~[Element]`. – Vladimir Matveev Jan 28 '14 at 07:45
  • @Vladimir True, thanks, but in my actual code `Element` is a trait, so I think I can't leave the `~` out in that case. – Kolja Jan 28 '14 at 20:51
  • 1
    Yes, you're right, in that case you will need the `~`. As for the question, I don't see how you could simplify the access to `Rc` box contents. That's how it works now. Hopefully, it would be simpler when real `Gc` will land (so no `Weak` wrappers would be needed), and when overridable dereference will be available (no extra `borrow()` and `borrow_mut()` calls). – Vladimir Matveev Jan 29 '14 at 06:36
  • OK, thanks the answer. I'm now contemplating simply using raw pointers to reference the container, since I know that it will exist for at least as long as the elements do (there is only one in the program, and it lives as long as `main` does). I will keep an eye on `Gc`s development, though. – Kolja Jan 29 '14 at 12:38

1 Answers1

1

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).

huon
  • 94,605
  • 21
  • 231
  • 225