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

How to use generic ViewMut iterator? #157

Closed
Saskapult opened this issue Dec 1, 2022 · 2 comments
Closed

How to use generic ViewMut iterator? #157

Saskapult opened this issue Dec 1, 2022 · 2 comments

Comments

@Saskapult
Copy link

Using shipyard 0.6.2

In the code below there are three functions.
The first function displays the expected behavior.
The second function yields a compilation error when trying to mutate x (x has type &T and not &mut T).
The third function yields a compilation error when iter() is called, with the error describing unsatisfied IntoAbstract trait bounds.
I am unsure of why this behavior occurs.

There was some discussion in #151 about using the get trait as generic.
As of yet I have been unable to solve my issue by referencing that discussion.
Why is this happening and what might I do to fix it?
I do not seem to be knowledgeable enough to resolve it out on my own.

use shipyard::*;

trait MutTrait {
    fn mutmut(&mut self);
}

#[derive(Component)]
struct ExampleComponent(u64);
impl MutTrait for ExampleComponent {
    fn mutmut(&mut self) {}
}

fn expected(
    mut v: ViewMut<ExampleComponent>,
) {
    for x in (&mut v).iter() {
        x.mutmut();
        println!("This is a mutable reference");
    }
}
fn weird<T: Component + MutTrait>(
    mut v: ViewMut<T>, 
) {
    for x in (&mut v).iter() {
        x.mutmut(); // Error (x is &T, not &mut T)
        println!("This is not a mutable reference");
    }
}
fn weirder<T: Component + MutTrait>(
    mut v: ViewMut<T>, 
) {
    for (x,) in (&mut v,).iter() { // Error (IntoAbstract trait bounds not satisfied)
        x.mutmut();
        println!("This is even weirder");
    }
}

The error produced by the third function:

the following trait bounds were not satisfied:
            `&(&mut shipyard::ViewMut<'_, T>,): IntoAbstract`
            which is required by `&(&mut shipyard::ViewMut<'_, T>,): shipyard::IntoIter`
            `&mut (&mut shipyard::ViewMut<'_, T>,): IntoAbstract`
            which is required by `&mut (&mut shipyard::ViewMut<'_, T>,): shipyard::IntoIter`
@leudz
Copy link
Owner

leudz commented Dec 1, 2022

Hi!

The "big problem" with &mut ViewMut and Get/IntoIter is the output type.
Based on the tracking, the output will not have the same type. For modification tracking the output will be Mut<T> and not &mut T.
Since specialization is not stable, ViewMut's Get and IntoIter are implemented for individual tracking types. As opposed to View where I can make a single implementation with &T output type.
There is one other type with a general implementation: &ViewMut.
All this combined leads to the compile errors you got.

In your first function you have a concrete type with a concrete tracking type. In this case your not tracking anything so Untracked.
The compiler checks if there is a function iter for &mut ViewMut<ExampleComponent, Untracked> and there is, no problem.

In your second function there is no concrete type, only a generic. So the compiler will try to find an iter function that works for all T in &mut ViewMut<T, T::Tracking>.
But because IntoIter is only implemented for specific tracking types, it can't find the implementation.
So it will expand the search and find &ViewMut<T, T::Tracking>, giving you &T.
To make the compiler able the find the right implementation you can use these bounds (you were on the right track with issues related to Get, I copied this and changed Get by IntoIter):

fn weird<T: Component + MutTrait>(mut v: ViewMut<T>)
where
    for<'a, 'b> &'a mut ViewMut<'b, T>: IntoIter,
    for<'a, 'b> <<&'a mut ViewMut<'b, T> as IntoIter>::IntoIter as Iterator>::Item:
        DerefMut<Target = T>,
{
    for mut x in (&mut v).iter() {
        x.mutmut();
        println!("This is a kind of a mutable reference");
    }
}

DerefMut<Target = T> will work both for &mut T and Mut<T> so all trackings are covered.
And you need mut x because of it.

For your third function it starts just like the second one. The compiler tries to find an iter that matches but there is none so it expands its search.
But as opposed to the second one, the compiler will not change (&mut T,) by (&T,), this not a coercion the compiler makes.
So you end up with no implementation at all and the compiler will point at IntoIter and IntoAbstract which are required to use iter.

I hope this helps understand the issue and how to fix it.
I'll definitely add a section or chapter to the guide about this so people can find it easier. And hopefully one day this won't be an issue.

@Saskapult
Copy link
Author

Thank you for the thorough explanation!

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