References to owner

Of course this is the hot item everywhere, but I need some tips or hints... (chat gpt is coming with the most insane statements "upgrade, downgrade" etc.).

lets take a very simple example from my "database metadata" exercise:

pub struct MetaData {
    tables: Vec<Metatable> // metadata is the owner of everything.
}

pub struct MetaTable {
    metadata: // a (weak) reference to the owner
    name: String
}

impl MetaData {
    fn AddTable(&mut self, name: &str) {
        // how to add in the simplest way a new table to self.tables with a ref to self.
    }
}

The &, Arc, Rc, Weak, 'liefetime are making me dizzy :slight_smile:
Sorry... coming from C# where you can do anything with anything.

I suggest you remove the metadata field and simply don't have a reference to the owner in the struct. Instead pass the owner (or parts of the owner) as parameters when needed. This may require refactoring, but that is expected when learning how to do things in Rust.

It is better not to try to bend Rust to the approaches you've used in other languages where "you can do anything with anything". Better to try to live within the limitations first, and resort to more complex mechanisms like Rc/Weak only when truly necessary.

6 Likes

It's impossible to use Rust's temporary references (&) for this. See "self-referential struct" problem.

The best way is to redesign your program so that you don't need this backreference at all. If you need more context to work on MetaTable, then explicitly pass additional data to functions needing it.

pub struct MetaData {
    tables: Vec<MetaTable>,
    extra_context: ExtraContext,
}

pub struct MetaTable {
    name: String
}

fn do_stuff(table: &mut MetaTable, extra_context: &ExtraContext);

Otherwise, you will need:

  • MetaTable.metadata to be std::arc::Weak<MetaData>.
  • Wrap MetaData in Arc<MetaData> when you create it.
  • Make MetaData.tables a Mutex<Vec<Metatable>> to be able to modify it from inside Arc.
6 Likes

I will have a look at that.

The point of the exercise project is exactly this. have safe references all over the place.
A table has fields, A table has indexes. An index has fields etc.
I'll start of with the above 3 and try these in 'table'.

One more question: as far as I know that is possible with a lifetime specifier isnt it??

No. I saved this quote from another thread today because I thought it explained it very clearly.

4 Likes

mutable or not. doesn't your anwer imply that lifetimes are a useless feature?!
i constructed local (succesfully) a struct with lifetiime refs.
of course that struct is killed when the function ends.

No, lifetimes are used for all kinds of things other than (attempting) creation of self-referential structs, circular references, etc. You are probably missing some basic info about ownership, borrowing and lifetimes. I'm sure others will explain more here and discuss it. But please do also go over the ownership chapter in the book if you haven't already. This will give you more context for discussing it in depth.

1 Like

Lifetimes are limiting but very beneficial nonetheless; the limitations allow the compiler to prove things like memory safety while still performing alias-based optimizations.

But it is true that Rust references (&'_ T, &'_ mut T) are not so useful for building self-referencial structures or other pointer-heavy data structures. The primary use-case of references are short-lived borrows.

I'm not sure if this is what you had in mind or not, but Rust lifetimes (those '_ things) are a compile time construct and don't directly correspond to the liveness scopes (often also called "lifetimes") of values (which is a dynamic property).

3 Likes

Very clear! And logical.

3 Likes

Rust doesn’t have a garbage collector that enables C# to have references from anywhere to anywhere. This flexibility has a cost of having very tricky edge cases, and as a result requiring a GC runtime.

To make no-GC memory management efficient and safe Rust had to sacrifice some flexibility, and only allow certain shapes of data that can be statically analyzed. Circular references and inter-dependencies between fields complicate an already complicated borrow checking.

C# design patterns that take advantage of GC flexibility just don’t fit Rust. Rust solves these issues differently. With Arc/Mutex/Weak you can make some circular structures and have GC-like memory management. Rust makes it syntactically noisy, because it wants to make cost and runtime behavior explicit.

8 Likes

That's what I have to learn. Rethink strategies.
I really like Rust. it is 'clean' and perforrms. And in some ways it is much more flexible / extendible. GC (and JIT) I never liked, although it makes life sometimes very easy.

2 Likes