Architecture Suggestions for Idiomatic Rust

TLDR; This is more a discussion than a plea for help. I am asking for feedback on how to organize three objects in relationship while keeping the borrowing rules under control. I have an idea, but it may not be best. I wanted to run it past more experiences rustaceans :smile:

Assumption: An immutable Token cannot have a field that references a mutable State.

After about 100 hours of really working in rust (not just doing the tutorials, but working on an actual project), I feel like I'm starting to think in rust and appreciate the borrow checker. I have an architecture decision to make and I don't just want to do what I did in Java.

So, I have a Token that is deserialized from a json manifest. None of its data ever needs to be mutated by anyone.

That Token can also have Storage, which is a growable byte heap. That data will be mutated by the user.

That Token also has a State which will not be mutated, but will be replaces as needed.

Each Token has exactly one Storage and State by relationship. Storage and State have no direct relationship.

The Token is kept in a TokenManager and references are handed out as needed. I wanted to keep the Token immutable, so I didn't have to worry about the borrow checker as much. However, in order to keep the Token immutable, I can't simply attach the Storage and State, that would mean mutating Token to replace State, and making Token mutable to have a field that is mutable (Storage).

So, my idea is to give each Token a unique id, and have a StateManager and a StorageManager to hold all states and storage. Then, on the Token object, add methods that simply call the StateManager and StorageManager.

There are some issues with this approach:

  1. I will either have clone the state and storage whenever it is returned, or fight the borrow checker.
  2. I can't have a reference to either the StateManager or StorageManager on the Token, or it would have to be mutable, which I don't want. So, I'll have to workout some static non-sense.

Specific Questions:

  • Is this a case for interior mutability?
  • How could I use static methods on the State and Storage Managers so the Token doesn't need a direct reference?
  • Is there an entirely different approach I should consider?

Solved. Here is my working concept: Architecture Suggestions for Idiomatic Rust - #7 by electricjones

1 Like

I will, of course, add and release the solution after the discussion. So the next person googling will have an easier time.

Instead of thinking in terms of mutability, think of references in terms of ownership. T is an unique owned value, &mut T is a unique non-owning reference to T, &T is a shared non-owning reference. The only reason why &T is immutable by default is because shared mutability is hard and should be dealt with separately (in the end, through an UnsafeCell).

In this way, do your Token own the State? How about the Storage? Should it always uniquely identify a State or Storage?

1 Like

Conceptually, yes. Each Token owns a state and a storage. Each token is also the sole owner of a state and a storage. And I need to be able to mutate the state and storage while, at the same time, passing around multiple shared references to a token. This (to my newbie mind) breaks rust.

I'm not sure what you mean by uniquely identify storage, but I think, yes.

I guess I was thinking it terms of mutability and immutability because that's what the compiler talks to me about: Cannot borrow x as mutable.

This is a case where you can use either an arena or interior mutability. Arenas allow you to have lots of references to data (in the form of ids, not &T) and mutate the data inside (provided that you don't need to modify two things concurrently)

You can use interior mutability like so

struct Token {
    data: Data,
    state: Cell<State>,
    storage: RefCell<Storage>
}

Then expose an api that only uses &self

Yeah, the compiler thinks in terms of uniqueness, but talks in terms of mutability. Mainly because this is easier for people to learn

3 Likes

Thank you, that helps. I think my original idea was an arena. The entirely separate StorageManager and StateManager would hold onto states and storages by id. Then, the token could just tell the manager update(id, key, value) or something. But, I wasn't sure how to have a reference to the managers in the token or how to make update() similar to a static method in php or java, where the static class can hold some state by itself.

In any case, I'm going to explore interior mutability. I'm also going to go through all my thoughts again to make sure that what I actually need is multiple references to the same shared-ownership Token. I realize that what I'm trying to do is what Rust seems to be built to avoid.

Maybe another 100 hours will get me closer :wink:

1 Like

With @RustyYato's help, I got a working concept. I am posting the snippet here for any future newcomers. I am still not sure this high level architecture is really what I want -- I feel like interior mutability should be a last resort. But, it will work for now while I continue to re-evaluate if I need to share anything that is mutable between the various references to Token.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.