Correct way of managing Rust structures


#1

After having some mid scale logic in Rust, I faced a problem with structs, here is the simple example

struct A {
      // Some fields for A struct
     b: B,  // object from B struct
}

impl A {
// implemented functions for A
    pub fn example_fn(&mut self) {
           // Some logic which should be called from function in C struct
    }
}

struct B {
      // Some fields for B struct
     c: C,  // object from C struct
}

impl B {
// implemented functions for B
}


struct C {
      // Some fields for C struct
}

impl C {
// implemented functions for C

    fn some_function(&mut self) {
        // How to call one of the A functions here ?
    }
}

So the question is, how to design structures so that it could be possible to call some function from A from function in C ?


#2

Oh, that’s tricky. In C you have no guarantee that it belongs to B or A (it could have been a standalone object), so there’s no way to go “up” from there.

It may also be hard to pass both A and mutable C together (fn some_function(&mut self, a: &A)), since A would be used both as unique reference (to share its inner C) and as a shared reference.

Rust can track precise field usage within a function, so perhaps move as much as you can to A:

impl A {
   fn some_function_c(&mut self) {
       self.b.c.something();
       self.something();
       self.b.c.etc();
   }
}

or split A into Aouter and Ainner, and make C use Ainner only:

struct A {
   ainner: AInner;
   b: B;
}

impl C {
   fn do(&mut self, a: &Ainner) {}
}

This way it’ll be possible to call a.b.c(&a.ainner).


#3

That looks quite a bit like a class hierarchy. I’m curious about the larger context here. Does A really need to be a struct, or would it make sense for it to be a trait? Maybe trait inheritance would be useful here.


#4

Thanks for replays, after I saw that it is really tricky to write code in Rust using OOP background, I started learning about trait's and impl's. So now I have this structure and all works perfectly

struct A {
}

trait B { // with some functions }
trait C {// with some functions}

impl B for A { // implementations here }
impl C for A { // implementations here }

This structure I liked more than class inheritance in OOP, hope it will be more performant in execution also :slight_smile: