Can't save / pass parent reference to composition object

In C ++ it is something like struct A

consists of struct B

, and some function B

takes a pointer to the parent object A

. Therefore, the function A

that calls this function B

simply passes to the pointer this

. I'm trying to do this in Rust, but I can't get it to work - this is what I want to achieve:

struct A<Type: T> {
    composition: Type,
    value: usize,
}

impl<Type> A<Type> where Type: T {
    fn new(obj: Type) -> A<Type> {
        A {
            composition: obj,
            value: 999,
        }
    }

    fn f(&mut self) {
        println!("Value: {:?}", self.value);
    }

    fn call(&mut self) {
        self.composition.f(&mut self);
    }
}

trait T {
    fn f(&mut self, &mut A<Self>);
}

struct B {
    value: usize,
}

impl B {
    fn new() -> B {
        B { value: 0, }
    }
}

impl T for B {
    fn f(&mut self, parent: &mut A<B>) {
        println!("B::f");
        parent.f();
    }
}

fn main() {
    let objA = A::new(B::new());

    // I want call sequence -> A::call() -> B::f() -> A::f()
    objA.call();
}

      

Note that I want mutability across all functions, although in the example above it might seem like it &mut self

doesn't make a lot of sense in most of the function parameters. How do I do this in Rust?

+3


source to share


1 answer


This may not work because you are violating the requirements for mutable anti-aliasing - you are trying to easily borrow A

its substructure at the same time:

self.composition.f(self);
// roughtly equivalent to:
let c = &mut self.composition;  // borrow substructure
c.f(self /* borrow self */);

      

(I removed the explicit &mut self

one because it is incorrect (as it gives you &mut &mut A<...>

, but it doesn't change the whole image at all.)

This is a natural fallacy in the Rust framework. Suppose the implementation f

on this particular composition X

overwrites the field composition

on the passed object:

impl T for X {
    fn f(&mut self, a: &mut A<X>) {
        a.composition = create_x_somehow();
    }
}

      



And suddenly, the object called by this method is destroyed and self

invalid!

Naturally, the compiler does not allow you to do this, even if you know you are not changing composition

, because such knowledge cannot be statically encoded (especially considering that this is a trait method that can be implemented by any user with access to your trait).

In situations like this, you essentially have two options:

  • reformulate the problem so that it no longer requires such an architecture, or
  • use special language / library constructs to deal with such static checks.

The second point is to use things like Cell

/ RefCell

(they are safe, i.e. they do not require blocks unsafe

, but they can panic at runtime - this may possibly work in your case) or, if nothing else helps, discarding the original pointers and code unsafe

. But honestly, the first option is generally better: if you build your code based on the semantics and alias rules applied by the compiler, the resulting architecture will almost always be of much better quality.

+2


source







All Articles