How to access to outer struct from nested struct?

even though I'm new to Rust, I already know that the following code block is totally incorrect, but I'm writing it anyway because I think it's clear what I'm trying to do:

#[derive(Debug)]
struct OData {
    of1: i32,
    of2: String,
    of3: IData,
};

#[derive(Debug)]
struct IData {
    if1: char,
    if2: String,
    if3: &mut OData,
}

I would like to be able to read and modify an instance of the OData struct from a struct nested in it. Can you help me? Thank you

If you're trying to create a data structure with "back links", where the IData points to the OData that owns it, you can't use references and in general you're probably going to have a bad time. That said, one way to do so is with a Weak Rc (or Arc).

Consider refactoring instead, so that your references are short-lived temporaries.

struct ODataInner {
    of1: i32,
    of2: String,
}

struct OData {
    inner: ODataInner,
    of3: IData,
}

struct IData {
    if1: char,
    if2: String,
}

impl OData {
    fn do_something(&mut self, thing: String) {
        self.of3.do_something(&mut self.inner, thing);
    }
}

impl IData {
    fn do_something(&mut self, parent: &mut ODataInner, thing: String) {
    }
}
1 Like

First of all, thank you for your time and your response.

I hadn't thought of such a refactoring, and from now on, I will add it to my toolbox.

However, it doesn't exactly meet the constraints imposed by my problem. Moreover, if you'd like, I am curious to understand the solution you mentioned (which seems to require the use of Weak Rc or Arc). Not only could it satisfy the constraints of the problem, but it could also be a source of further knowledge for me.

In any case, after a quick and superficial reflection, I realize that using smart pointers would require the data to reside on the heap (at the moment, I'm not sure if this could be a problem in my case).

Thank you anyway, regardless of whether you respond to me or not.

By the way, I posted the same question on Stack Overflow with the title: "How to read and modify fields of a parent struct from a nested struct in Rust?" but it was closed as a duplicate of the question: "Why can't I store a value and a reference to that value in the same struct?" It doesn't seem like the same thing to me

You may be hitting mistake 3 in this list.

Anyway, this chapter of the book demonstrates making a tree with parent backlinks, for example.

When it typically comes up, the scenario looks something like this

 struct S<'a>
+-------------+
| String      |<----.
+-------------+     |
| &'a str     |-----'
+-------------+

And one can actually construct such a self-referential struct in safe Rust. But in order to do so, the struct must be exclusively borrowed for the rest of it's validity (you have to acquire a &'a mut S<'a>), which is so restrictive it's pretty much never practical: once created, you can only use the struct via that borrow. You can't directly call methods on it, you can't take new references to it, you can't run a non-trivial destructor if it has one, and you can't even move it.

All of those would violate the exclusivity of the &'a mut S<'a>.

But it makes complete sense you can't move it independently of Rust's borrow rules -- the &str would immediately dangle! (Rust has no move conscructors).


Now, your example is somewhat different. One difference is that you've put a couple fields in their own struct.

  OData<'a>
+---------------+
|   IData<'a>   |
| +-----------+ |
| | ...       | |
| +-----------+ |
| ...           |
+---------------+

But that's not really material here, so let's flatten it out, and look at the bigger differences:

                ,-------.
                |       |
 OData<'a>      v       |
+------------------+    |
| Other stuff      |    |
+------------------+    |
| &'a mut OData    |----'
+------------------+

The two differences from the previous example are

  • You want an exclusive reference (&mut)
  • You want to reference to be to the entirety of the OData

If you just had an exclusive reference to some other field, it could still be constructed in safe Rust, though it would be even more restrictive than the shared reference case. But because you're referencing the entirety of &'a mut OData, there's no sound way to construct this at all. You would need a &'a mut OData to assign to the field, and also a way to mutate the field to perform the assignment. But the &'a mut OData has exclusive access to the field while it is active -- and it has to be active when you move it (assign it to the field). If you constructed it unsafely, it would probably be instant UB, but if not it would definitely be unsound: You could create aliasing &mut OData recursively through the field.

(Plus it has the same "dangles if moved" problem.)

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.