I’m working on a system that amongst others uses WriteStorage<MyComponent> in SystemData.
Therefore, all components of that type are being passed to the run-method as the parameter mut my_components.
Now I have some code that specifies two different entities that have a MyComponent attached to them: let e1: Entity = ... let e2: Entity = ...
With my_components.get_mut(e1) (or with e2) I can obtain a mutable reference to the associated component; for normal Rust-borrow-checker-reasons, the two mutable references cannot be sustained at once; that is, however, exactly what I need.
If faced with a standard Collection, there is split_at_mut that lets you deal with these kind of problems; that doesn’t work with the Storage-collections though.
How does one solve this problem? Transferring the Storage into, say, a Vec (which provides split_at_mut) seems a bit elaborate. Is there a simpler approach?
Since my original post, I have tried several approaches, here are my experiences with them:
I’m not sure whether I’m understanding you correctly, but those are my two interpretations of this approach:
anything involving cloning: that does work, and as I am not working in a performance-critical part of my program, this is actually okay. It doesn’t feel right though, as I don’t really want to juggle with new components.
saving the two references, transferring the storage into something with split_at_mut, then finding the saved references in there: as the components are referenced immutably, there may not be a mutable call to the storage, which kills this approach right away (I am no Rust-pro by far, maybe there’s a different way?).
That seems quite like the way to go; amethyst and specs don’t document this part of their API very well and I barely have a clue what to consider when working with the storage. But I surely will do some more digging.
Implementing a custom trait for Storage doesn’t give me access to its private fields, or am I wrong? Or do you mean, that I should edit Storage's actual source code right in place? Anyway, that would still leave me with my insufficient knowledge of Storage's internal workflows…
Lastly, I have tried simply removing the components from the storage, working with them, and at the end of run, I simply re-insert them. As with the clone-approach, this seems a bit overkill, but I think it’s more favourable because no large data copying is involved. I made a brief performance test:
get_mut twice, the mutating operation separated so there’s no borrow-conflict:
hovered around 50μs
remove both components, then the one, correct mutating operation on both of them, then insert:
fluctuated a lot, averaging around 70μs I’d say.
As I’d said, this is not very performance-critical code; but even if it was, the impact of remove/insert is barely noticeable in my context and I think, this will be the way to go for me. I wonder if this solution is safe concerning specs’ parallelism.
Your approach seems quite reasonable, both in performance and coding labour.
I could definitely implement this for my case;
the function I want to call (fn function(&mut self, other: &mut MyComponent); in MyComponent) presents a layer of abstraction I want to keep though. Inside my system, I don’t really want to get into the details of MyComponent, I just want to call that function.
I think I’ll stick with the remove-insert approach for the moment; if there’s a way to correctly obtain mutable references to two components, I would still like to know.