Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Component iteration without modification tracking #182

Open
parasyte opened this issue Sep 7, 2023 · 2 comments
Open

Component iteration without modification tracking #182

parasyte opened this issue Sep 7, 2023 · 2 comments

Comments

@parasyte
Copy link

parasyte commented Sep 7, 2023

Since 0e2d67a, IntoIter now always yields Mut<T> for mutable components. In the latest published release, this was only true for components with tracking enabled, e.g. using the derive macro with #[track(Modification)].

There is one big problem with this new approach because field accesses to mutable components go through DerefMut which is not supported by two-phase borrows with operator traits like AddAssign. See:

Any component that has a struct field with a bitwise assignment operator implementation will have borrow check errors for code that normally wouldn't (without the DerefMut). An example using glam::Vec2 for its AddAssign impl:

use glam::f32::Vec2;
use shipyard::{Component, IntoIter as _, ViewMut, World};

#[derive(Component, Debug)]
struct Calc {
    vec: Vec2,
    scale: f32,
}

fn main() {
    let world = World::new();

    world.run(|mut calc: ViewMut<Calc>| {
        for mut calc in (&mut calc).iter() {
            calc.vec += calc.scale;
        }
    });
}

This works on the released version (0.6.2, with a warning about an unnecessary mutable variable), but fails on git HEAD:

error[E0502]: cannot borrow `calc` as immutable because it is also borrowed as mutable
  --> src\main.rs:15:25
   |
15 |             calc.vec += calc.scale;
   |             ------------^^^^------
   |             |           |
   |             |           immutable borrow occurs here
   |             mutable borrow occurs here
   |             mutable borrow later used here
   |
help: try adding a local storing this...
  --> src\main.rs:15:25
   |
15 |             calc.vec += calc.scale;
   |                         ^^^^^^^^^^
help: ...and then using that local here
  --> src\main.rs:15:13
   |
15 |             calc.vec += calc.scale;
   |             ^^^^^^^^^^^^^^^^^^^^^^

The workaround is trivial, just bind calc.scale to a temporary variable and add-assign with that on the right-hand side. But this is a pretty clear breaking change, and also a gnarly UX.

It would be nice to be able to get &mut Calc out of the iterator instead of Mut<Calc>. Both because it causes this issue, and because I don't need mutation tracking. That sounds like unnecessary overhead for default iteration behavior, doesn't it?

I am not sure what motivated the change, maybe #157 (comment) had something to do with it?

@leudz
Copy link
Owner

leudz commented Sep 8, 2023

The main motivations are listed here: https://shipyard.zulipchat.com/#narrow/stream/269448-update/topic/On.20demand.20tracking/near/325747110.
I agree it's not perfect, Mut is annoying and a cargo feature can't be added to solve the problem.

I think this is the third or forth iteration on the modification tracking design. So far they all come with some big problem but some of them can't even be worked around.

@parasyte
Copy link
Author

parasyte commented Sep 8, 2023

Ah I didn't know there was a zulip. 👀 I'll see you there!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants