resolving cyclic dependency between traits that need references to each other in rust

Viewed 193

I have two structs that need references to each other. Due to other reasons these structs need to bypass borrow checker at the same time. So I have a wrapper around *mut T to bypass the checker. Now I am trying to have each struct have a generic type of the second struct.

This is a toy example:

use d2simrs::util::internalref::InternalRef;

pub trait Layer3Trait {
    fn foo() {
        println!("TraitA::foo()");
    }
}

pub trait Layer2Trait {
    fn bar() {
        println!("TraitB::foo()");
    }
}

pub struct SimpleLayer2<Layer3T>
    where Layer3T: Layer3Trait
{
    pub layer3: InternalRef<Layer3T>,
}

pub struct SimpleLayer3<Layer2T>
    where Layer2T: Layer2Trait
{
    pub layer2: InternalRef<Layer2T>,
}

pub type Layer2 = SimpleLayer2<Layer3>;
pub type Layer2Ref = InternalRef<Layer2>;

pub type Layer3 = SimpleLayer3<SimpleLayer2<Layer3>>;
pub type Layer3Ref = InternalRef<Layer3>;

Code for InternalRef is here

For which I get

   |
30 | pub type Layer3 = SimpleLayer3<SimpleLayer2<Layer3>>;
   |                                             ^^^^^^
   |
   = note: ...which again requires computing type of `example2::Layer3`, completing the cycle
note: cycle used when computing type of `example2::Layer2`
  --> examples/network/bypass_borrow/example2.rs:27:32
   |
27 | pub type Layer2 = SimpleLayer2<Layer3>;
   |                                ^^^^^^

Can I somehow re-define something, so that SimpleLayer2 and SimpleLayer3 can have references to one another.

1 Answers

Rust does not support cyclic generic types. If you have:

pub type Layer2 = SimpleLayer2<Layer3>;
pub type Layer3 = SimpleLayer3<Layer2>;

Then the concrete type of Layer3 would be:

SimpleLayer3<SimpleLayer2<SimpleLayer3<SimpleLayer2<SimpleLayer3<SimpleLayer2<SimpleLayer3<SimpleLayer2<SimpleLayer3<SimpleLayer2<SimpleLayer3<...>>>>>>>

Which is not allowed.


As nudged at in the comments, it would ideal if your design were heirachial with no cyclic dependencies since it would obviously avoid such problems. But if that is not appropriate, the standard way to solve this would be with Rc/Arcs and trait objects, which would also avoid your internal *mut T wrapper (with RefCell/RwLock if you need mutability):

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

pub struct SimpleLayer2 {
    pub layer3: Weak<RefCell<dyn Layer3>>,
}

pub struct SimpleLayer3 {
    pub layer2: Rc<RefCell<dyn Layer2>>,
}

See these other questions for other potential ideas:

Related