ss7
ss7

Reputation: 3012

Idiomatic Way to Handle Struct Fields that Reference the Parent

While porting an application from Python to Rust I ran into this problem.

If we have classes like this:

class Backend:
    def __init__(self, result_consumer=None):
        self.result_consumer = ResultConsumer(self, ..)
    
class ResultConsumer:
    def __init__(self, backend=None):
        self.backend=backend

and ResultConsumer is regularly calling methods like self.backend.method() and Backend is also calling methods like self.result_consumer.method().

In Rust you can't just use struct fields to establish such a relationship.

I tried an approach with generics (as ResultConsumer should support multiple backends)

struct ResultConsumer<B: Backend> {
    backend: Arc<B>,
}
struct Backend {
    result_consumer: ResultConsumer<Self>
}

what would be the most idiomatic way to represent this relationship in Rust? Is my example approach viable?

I am asking specifically, because while it compiles I cannot figure out how to populate the ResultConsumer in the result_consumer field for Backend when I construct it.

Upvotes: 1

Views: 240

Answers (1)

asmmo
asmmo

Reputation: 7100

If I understand you well, you can use something like the following

struct ResultConsumer {
    backend: RefCell<Weak<Backend>>,
}
struct Backend {
    result_consumer: RefCell<Rc<ResultConsumer>>,
}

impl Backend {
    fn new() -> Rc<Backend> {
        println!("Backend::new");
        let mut b_rc = Rc::new(Backend {
            result_consumer: RefCell::new(Rc::new(ResultConsumer {
                backend: RefCell::new(Weak::new()),
            })),
        });

        *b_rc.result_consumer.borrow().backend.borrow_mut() = Rc::downgrade(&b_rc);
        b_rc
    }
}

Upvotes: 1

Related Questions