A question regarding state sharing layout

@2e71828 OK, so here is my proposal for a merge of mine and your solution. What do you think? For me it all looks like it would need tons of comments in order to enable me to understand that, even today evening. But it seems to work and if I'm right it combines thread safety, shared access plus small Clone costs.

May I ask for your opinion?

playground

1 Like

Yes, that looks about right. Of course, this is overkill for such a small State, but your real problem is significantly bigger.

1 Like

Perfect. Many thanks again.

I'm sorry, just one last question, I promise: Is it good Rust style to have the ComponentA relevant setters in the ComponentA? I mean, instead of calling increment(x) on ComponentA I could directly update the counter.

So

pub fn inc_a(&mut self, x:u32) {
        Arc::make_mut(&mut self.a).0 += x
}

instead of

pub fn inc_a(&mut self, x:u32) {
        Arc::make_mut(&mut self.a).increment(x);
}

Does the same, is less verbose. Anything agains that from Rust best practices?

I wouldn't be surprised at seeing either in production code-- The second is a bit more flexible because things that only depend on ComponentA don't have to hold a reference to the entire state.

Your experience here is quite common. Many of us pick up Rust as "just another language" and find that we have to develop a substantively new methodology for thinking about programming, shifting from a more imperative "what to do next" model to a more data-ownershio-based one.

1 Like

So true...

I'm a bit surprised now. Rust was again not matching my expectations. I'm having uploaded two versions of the State holder, one with RwLock and Arc and one just with RwLock. I was expecting the first to run faster due to the "copy_on_write" policy and the improved Clone. But in fact, at least on my machine the "RwLock-only" version runs faster.

Test is: Having two concurrent threads updating one and the same variable and one additional thread reading it from time to time.

What do I miss? Is the advantage of the Arc solution not visible due to the small structure, so that the possible speed advantage is negatively over-compensated by the Arc overhead?

With RwLock and Arc

With RwLock only

I know a little secret about Rust that might help you....

If you have ever programmed in C, C++, PL/M, Coral, Pascal, even Ada and no doubt others, and especially if you have used threads in those languages, then you have learned more about Rust than you might think. Not from the syntax and semantics of those languages but through long hours of frustration debugging programs you have written in them.

You likely learned some rules to try and ensure robust programs:

Never return pointers to local variables from a function - dangling pointer.
Never dereference a null pointer.
Always free memory before forgetting the last pointer to it - memory leak.
Never free memory twice.
Never allow two or more threads to mutate data through a pointer without some mutual exclusion - race condition.
Never push onto a Vector in C++ and expect any references to elements within it to work - undefined behavior.
Be careful with type coercions and casts - loss/corruption of data.
Endianness can be a killer.

And many more.

The secret then is that Rust simply enforces all those "unwritten rules" that you already know, at compile time.

1 Like

Yes, I know. But that doesn't make me less puke sometimes :slight_smile: You have an idea, about how things should work, but the compiler constantly refuses to follow. But I don't complain, really. I'm fine with that.

I do feel the frustration.

What I generally find is that after a long stint of tearing my hair out and finally getting something to compile, if I stand back and look at what happened I slap my head with the realization that what I was originally writing would have been a disaster had I written much the same in C (for example). C may well have let me do it and I it may have worked and I would have been happy. Until the error of my ways shows up when using a different optimization level, or compiler, or architecture, or just different phase of the moon.

Just keep trying to remember that when you come out of the end of of all the trials and tribulations what you have created will be rock solid.

2 Likes

When you use Arc, you really have to ask yourself if it makes logically sense to use it. In your example, you use Arc as a standalone smart pointer, i.e. you only use it for cloning. When does Arc's cloning become cheaper than direct cloning? The short answer is, whenever a direct clone would involve an allocation on the heap.

The plain data needed for Arc to become more efficient than a copy is likely over 1 KiB. Arc requires synchronization and that does take a bit of time.

3 Likes

@Phlopsi That's a great and logical explanation and makes something clear to me. Thanks for this.

Oh, thanks for the cheer up. My C days are long, long ago, but I remember the principles at least. And yes, you are right: The reward is worth it. It is just so, if you are in the beginning (and with Rust I'm dealing a week or so), there are so many open ends and every now and then you are going to do SODD :slight_smile: (whereas SO is probably not the right source of information here, but this forum is). Last time I wrestled so hard was with Swift a couple of years ago. In the meantime I felt pretty comfortable with Java an JavaScript.

Thanks for all the educated and helpful comments.

1 Like

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.