Back reference to the parent struct


#1

I have some nested structs and cannot create a back reference to the parent struct
An example:

struct Foo<'a>
{
    parent: &'a Bar<'a>,
}

impl<'a> Foo<'a>
{
    fn new(parent: &'a Bar) -> Self
    {
        Foo{
            parent: parent,
        }
    }
    
    fn hello_world(&self) -> String
    {
        self.parent.hello().to_owned() + " world"
    }
}

struct Bar<'b>
{
    child: Option<Foo<'b>>,
    data: &'static str,
}

impl<'b> Bar<'b>
{
    fn new() -> Self
    {
        Bar {
            child: None,
            data: "hello",
        }
    }
    
    fn hello(&self) -> &str
    {
        self.data
    }
    
    fn get_foo(&self) -> Option<&Foo>
    {
        self.child.as_ref()
    }
}

fn main() {
    let bar = Bar::new();
    assert_eq!("hello", bar.hello());
    match bar.get_foo() {
        Some(foo) => assert_eq!("hello world", foo.hello_world()),
        None => (),
    }
}

How can I replace None with Some<Foo> with a reference to Bar? So far I’m not sure that it is possible.


#2

So I am still learning myself and here are my thoughts:
I think you are right with regular reference types it’s not really possible. To do what you want you will have to mutate a structure while keeping the reference to it somewhere else (in your case another structure) and that’s a big no no in Rust.
Rust was specifically designed to prevent this type of behavior (yey memory safety, yey no data races)
Rust offers raw pointers, which you can use to do stuff like that, or you can play with Rc, Arc smart pointers.


#3

Yes, this is not going to be possible like this. What happens when Bar moves? Foo would then be invalid.

The http://crates.io/crates/owning_ref crate can help in some circumstances.


#4

Hi! I am browsing quite some Rust Q&A recently, because I am trying to learn this language. Most of the time I see answers like “that will not work”, but do not see an advice like “here is how we do that in Rust”. Do you know who are the people to go to for those advices? I understand that it is good to keep the community small while having so many moving parts in the language, but as far as I can tell, most of the people coming from an OOP background do not find the set of patterns that are to be used in this language.

For example the composite pattern is quite popular in OOP design. How to implement something like that in Rust?


#5

Part of the problem is, questions are often asked in an abstract way, so there’s not enough information given to say “this should be done like this”.

So for example here, this is entirely abstract, with Foo and Bar. So it’s not clear what’s actually being done.


#6

I’ve found that a lot of crates is the best source to understand Rust. You can see how others do it.

Rust composition example (at playground). I hope it will be useful:

#[derive(Clone)]
enum Radius {
    R15,
    R16,
    R17,
}

struct Wheel {
    radius: Radius,
}

struct Bridge {
    left: Option<Wheel>,
    right: Option<Wheel>,
}

impl Bridge {
    fn new(left: Radius, right: Radius) -> Self {
        Bridge {
            left: Some(Wheel { radius: left }),
            right: Some(Wheel { radius: right }),
        }
    }
}

trait Vin {
    fn code(&self) -> &str;
}

struct PetroleumEngine {
    vin: String,
}

impl Vin for PetroleumEngine {
    fn code(&self) -> &str {
        &self.vin
    }
}

struct GasEngine { }

struct ElectricEngine { }

struct Car<T> {
    front: Bridge,
    rear: Bridge,
    engine: T,
}

impl<T> Car<T> {
    fn new(engine: T, radius: Radius) -> Self {
        Car {
            front: Bridge::new(radius.clone(), radius.clone()),
            rear: Bridge::new(radius.clone(), radius.clone()),
            engine: engine,
        }
    }
}

impl<T: Vin> Vin for Car<T> {
    fn code(&self) -> &str {
        self.engine.code()
    }
}

fn main() {
    let engine = PetroleumEngine {
        vin: "ABC1234567890".to_owned(),
    };
    let mut car = Car::new(engine, Radius::R16);
    println!("VIN: {}", car.code());

    // Take front-left wheel and drop it
    let wheel = car.front.left.take();
    drop(wheel);
}

#7

Actually, the composite pattern is somewhat different. For example widgets on the user interface either contain other widgets, or they do not contain anything. And you want to treat both the same way. I got inspired by Ricardo’s article and I will post soon an implementation for the composite pattern.


#8

So, after much thought, I decided forget about parent struct owning child. Thank all for responding! =)