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

[Merged by Bors] - Remove broken DoubleEndedIterator impls on event iterators #7469

Conversation

tim-blackbird
Copy link
Contributor

@tim-blackbird tim-blackbird commented Feb 2, 2023

The DoubleEndedIterator impls produce incorrect results on subsequent calls to iter() if the iterator is only partially consumed.

The following code shows what happens

fn next_back_is_bad() {
    let mut events = Events::<TestEvent>::default();
    events.send(TestEvent { i: 0 });
    events.send(TestEvent { i: 1 });
    events.send(TestEvent { i: 2 });
    let mut reader = events.get_reader();
    let mut iter = reader.iter(&events);
    assert_eq!(iter.next_back(), Some(&TestEvent { i: 2 }));
    assert_eq!(iter.next(), Some(&TestEvent { i: 0 }));
    
    let mut iter = reader.iter(&events);
    // `i: 2` event is returned twice! The `i: 1` event is missed. 
    assert_eq!(iter.next(), Some(&TestEvent { i: 2 }));
    assert_eq!(iter.next(), None);
}

I don't think this can be fixed without adding some very convoluted bookkeeping.

Migration Guide

ManualEventIterator and ManualEventIteratorWithId are no longer DoubleEndedIterators.

@tim-blackbird
Copy link
Contributor Author

Not super sure what to write for the Migration Guide

Comment on lines -272 to +274
if scale_factor_events.iter().next_back().is_some() || ui_scale.is_changed() {
if !scale_factor_events.is_empty() || ui_scale.is_changed() {
scale_factor_events.clear();
Copy link
Contributor Author

@tim-blackbird tim-blackbird Feb 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.next_back().is_some() wasn't the right way to do this, but unless multiple scale_factor_events are sent in a frame it behaves the same way as is_empty with a clear.
Not a big deal.

@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior A-ECS Entities, components, systems, and events M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide labels Feb 2, 2023
@james7132 james7132 self-requested a review February 3, 2023 05:35
Copy link
Member

@JoJoJet JoJoJet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good fix. The migration guide should mention that the impls didn't work correctly, and that any code using this was likely broken.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Feb 5, 2023
@alice-i-cecile
Copy link
Member

bors r+

bors bot pushed a commit that referenced this pull request Feb 5, 2023
The `DoubleEndedIterator` impls produce incorrect results on subsequent calls to `iter()` if the iterator is only partially consumed.

The following code shows what happens
```rust

fn next_back_is_bad() {
    let mut events = Events::<TestEvent>::default();
    events.send(TestEvent { i: 0 });
    events.send(TestEvent { i: 1 });
    events.send(TestEvent { i: 2 });
    let mut reader = events.get_reader();
    let mut iter = reader.iter(&events);
    assert_eq!(iter.next_back(), Some(&TestEvent { i: 2 }));
    assert_eq!(iter.next(), Some(&TestEvent { i: 0 }));
    
    let mut iter = reader.iter(&events);
    // `i: 2` event is returned twice! The `i: 1` event is missed. 
    assert_eq!(iter.next(), Some(&TestEvent { i: 2 }));
    assert_eq!(iter.next(), None);
}
```

I don't think this can be fixed without adding some very convoluted bookkeeping.

## Migration Guide
`ManualEventIterator` and `ManualEventIteratorWithId` are no longer `DoubleEndedIterator`s.



Co-authored-by: devil-ira <[email protected]>
@bors
Copy link
Contributor

bors bot commented Feb 5, 2023

Build failed:

@alice-i-cecile
Copy link
Member

bors r+

bors bot pushed a commit that referenced this pull request Feb 5, 2023
The `DoubleEndedIterator` impls produce incorrect results on subsequent calls to `iter()` if the iterator is only partially consumed.

The following code shows what happens
```rust

fn next_back_is_bad() {
    let mut events = Events::<TestEvent>::default();
    events.send(TestEvent { i: 0 });
    events.send(TestEvent { i: 1 });
    events.send(TestEvent { i: 2 });
    let mut reader = events.get_reader();
    let mut iter = reader.iter(&events);
    assert_eq!(iter.next_back(), Some(&TestEvent { i: 2 }));
    assert_eq!(iter.next(), Some(&TestEvent { i: 0 }));
    
    let mut iter = reader.iter(&events);
    // `i: 2` event is returned twice! The `i: 1` event is missed. 
    assert_eq!(iter.next(), Some(&TestEvent { i: 2 }));
    assert_eq!(iter.next(), None);
}
```

I don't think this can be fixed without adding some very convoluted bookkeeping.

## Migration Guide
`ManualEventIterator` and `ManualEventIteratorWithId` are no longer `DoubleEndedIterator`s.



Co-authored-by: devil-ira <[email protected]>
@bors bors bot changed the title Remove broken DoubleEndedIterator impls on event iterators [Merged by Bors] - Remove broken DoubleEndedIterator impls on event iterators Feb 5, 2023
@bors bors bot closed this Feb 5, 2023
@tim-blackbird tim-blackbird deleted the event-not-doubleendediterator branch February 5, 2023 17:41
bors bot pushed a commit that referenced this pull request Feb 16, 2023
# Objective

Motivated by #7469.

`EventReader` iterators use the default implementations for `.nth()` and `.last()`, which includes iterating over and throwing out all events before the desired one.

## Solution

Add specialized implementations for these methods that directly updates the unread event counter and returns a reference to the desired event.

TODO:

- [x] Add a unit test.
- [x] ~~Add a benchmark, to see if the compiler was doing this automatically already.~~ *On second thought, this doesn't feel like a very useful thing to include in the benchmark suite.*
myreprise1 pushed a commit to myreprise1/bevy that referenced this pull request Feb 18, 2023
# Objective

Motivated by bevyengine#7469.

`EventReader` iterators use the default implementations for `.nth()` and `.last()`, which includes iterating over and throwing out all events before the desired one.

## Solution

Add specialized implementations for these methods that directly updates the unread event counter and returns a reference to the desired event.

TODO:

- [x] Add a unit test.
- [x] ~~Add a benchmark, to see if the compiler was doing this automatically already.~~ *On second thought, this doesn't feel like a very useful thing to include in the benchmark suite.*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Bug An unexpected or incorrect behavior M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants