How to organize editable but connected structs?

I have main struct that keeps the "raw" one with data, and in main struct, items keep Arc refs to items of the nested struct. It turns out that this way, the data not editable.

How to allow edits? Any suggestions on how to organize the structs are welcome.

struct RawItem { data: i32 };
struct RawData {
    items: Vec<RawItem>
}

struct RefinedItem {
    raw: Arc<RawItem>,
    refined_data: i32
}
struct RefinedData {
    nested: RawData,
    items: Vec<RefinedItem>
}
impl RefinedData {
    pub fn query(&self, key: usize) -> i32 {
        self.items[key].get_raw_data()
    }
}
impl RefinedItem {
    pub fn get_raw_data(&self) -> i32 {
        self.raw.data
    }
}

The data is organized in a way similar to a database, but I have to store it in memory, for performance reasons.

I tried another way, but it looks inconvenient: to keep RefinedItem and RefinedData without Raw* structs, but requiring Raw* as parameters to queries. This escalated in methods having to carry those everywhere and either also switch to Result<..> everywhere, or do unwrap-s.

struct RefinedItem {
    item_key: usize,
    refined_data: i32
}
struct RefinedData {
    items: Vec<RefinedItem>
}
impl RefinedData {
    pub fn query(&self, raw: &RawData, key: usize) -> i32 {
        self.items[key].get_data(raw)
    }
}
impl RefinedItem {
    pub fn get_raw_data(&self, raw: &RawData) -> i32 {
        raw.get(self.item_key).unwrap().data // << this kind of query has to be done in many functions
    }
}

I tried to wrap things in Arc<Mutex<T>>, but compiler was never happy even with simplest examples.

Have you tried Arc<Mutex<_>> or Arc<RwLock<_>>? Edit - missed your last sentence, but not sure why that wouldn't work

1 Like

What do you mean by "the compiler was never happy" with Mutexs?

I generally just use Rc<RefCell<...>>.

As shown above, you've discovered Arc<Mutex<...>>. Are your errors Send/Sync related ?

1 Like

I could not get the access method right, but while writing a playground example, I looked some examples up, and it worked.

working example

Are there different trade-offs or limitations between Rc<RefCell<T>> and Arc<Mutex<T>>?

Rc<RefCell<T>> can only be accessed by a single thread.
Arc<Mutex<T>> can be accessed by multiple threads, but has additional restrictions to make that happen.

2 Likes

I wrote a more elaborate sample, and it seems to work.

Last question: if I keep this struct in Axum server, and an API method tries to get write lock while the struct is accessed in another request, will it return Err(..)?

Quoting Mutex in std::sync - Rust

This function will block the local thread until it is available to acquire the mutex. Upon returning, the thread is the only thread with the lock held. An RAII guard is returned to allow scoped unlock of the lock. When the guard goes out of scope, the mutex will be unlocked.

The exact behavior on locking a mutex in the thread which already holds the lock is left unspecified. However, this function will not return on the second call (it might panic or deadlock, for example).

Errors

If another user of this mutex panicked while holding the mutex, then this call will return an error once the mutex is acquired.

1 Like

Be very careful using Mutexs with asynchronous code. Make sure you never hold a lock across await points, or you could deadlock your program. Read up on Tokio's guide to sharing state.

1 Like