1

I have two structs that depend on each other. In C++ I would do this with pointers, I'm trying to figure out how to do this in Rust. I've tried using Box and Rc so far, I would think since Rc is a reference counter it should be able to handle this, but it's giving me an error.

Here is a simple code example:

struct A {
    b : Rc<B>
}

struct B {
    a : Option<Rc<A>>
}

fn main() {

    let mut b = B {
        a : None
    };

    let a = A {
        b: Rc::new(b)
    };

    b.a = Some(Rc::new(a));

}

Here is the error I get from it:

20 |     let mut b = B {
   |         ----- move occurs because `b` has type `B`, which does not implement the `Copy` trait
...
25 |         b: Rc::new(b)
   |                    - value moved here
...
28 |     b.a = Some(Rc::new(a));
   |     ^^^ value partially assigned here after move

What is the correct way to do this type of relationship in Rust?

Rocky Pulley
  • 22,531
  • 20
  • 68
  • 106
  • You probably want to use an interior mutability pattern with `RefCell`. There's examples in the book – possum Nov 16 '22 at 01:48
  • 1
    `Rc` also isn't going to solve the problem by itself as you saw. It needs to own it's contents and doesn't provide a mutable reference. You need to use the immutable reference with `RefCell` – possum Nov 16 '22 at 01:50
  • Relevant: https://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that-value-in-the-same-struct – Jmb Nov 16 '22 at 07:30

2 Answers2

7

You shouldn't use Rc::new twice for an object. The correct way is to use Rc::new once, and clone it as needed. What's more, in order to mutate b behind a Rc, you should combine it with RefCell.

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

struct A {
    b: Rc<B>,
}

struct B {
    a: RefCell<Option<Rc<A>>>,
}

fn main() {
    let b = Rc::new(B {
        a: RefCell::new(None),
    });
    let a = Rc::new(A { b: b.clone() });

    *b.a.borrow_mut() = Some(a.clone());

    assert!(b.a.borrow().is_some());
}

But even you do like this, you still make an memory leak, which is bad. A better way is to use Weak and Rc::new_cyclic to make cycles.

use std::rc::{Rc, Weak};

struct A {
    b: Rc<B>,
}

struct B {
    a: Weak<A>,
}

fn main() {
    let a: Rc<A> = Rc::new_cyclic(|a| A {
        b: Rc::new(B { a: a.clone() }),
    });
    let b: Rc<B> = a.b.clone();
}

This avoids use of cells and memory leak.

Peng Guanwen
  • 547
  • 3
  • 9
0

This is compiling (this is single threaded example)

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

// make a cycle: struct A owning struct B and struct B owning struct A
struct A {
    b :  Rc<RefCell<B>>
}

struct B {
    a :  Option<Rc<RefCell<A>>>
}

fn main() {

    // init b with None
    let b = Rc::new(RefCell::new(B { a: None }));

    // init a with b
    let a = Rc::new(RefCell::new(A { b: Rc::clone(&b) }));

    // set b.a to a
    b.borrow_mut().a = Some(Rc::clone(&a));
}

You use Rc to have mutiple ownership. So that you can both move b to a and then still address it as b and even try change it.

Rc is read only so use RefCell so that you hide the fact that you change things.

There is another example of cycle in Rust book https://doc.rust-lang.org/book/ch15-06-reference-cycles.html

Nikolay Zakirov
  • 1,505
  • 8
  • 17