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

RangeInclusive::contains doesn't consider exhaustion #77941

Closed
cuviper opened this issue Oct 14, 2020 · 4 comments · Fixed by #78109
Closed

RangeInclusive::contains doesn't consider exhaustion #77941

cuviper opened this issue Oct 14, 2020 · 4 comments · Fixed by #78109
Labels
C-bug Category: This is a bug. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@cuviper
Copy link
Member

cuviper commented Oct 14, 2020

I tried this code:

fn main() {
    let mut range = 0..=0;
    assert!(!range.is_empty());
    assert!(range.contains(&0));

    range.next();
    assert!(range.is_empty());
    assert!(!range.contains(&0));
}

I expected to see this happen: all assertions pass.

Instead, this happened: the last assertion fails.

Meta

Playground link

The behavior is the same across stable 1.47.0, 1.48.0-beta.2, and 1.49.0-nightly (2020-10-13 adef9da30f1ecbfeb813).

Analysis

The implementation of is_empty includes a check for the exhausted flag:

    pub fn is_empty(&self) -> bool {
        self.exhausted || !(self.start <= self.end)
    }

But contains only looks at the raw bounds:

    pub fn contains<U>(&self, item: &U) -> bool
    where
        Idx: PartialOrd<U>,
        U: ?Sized + PartialOrd<Idx>,
    {
        <Self as RangeBounds<Idx>>::contains(self, item)
    }

It would be easy to insert !self.exhausted && ... in there, as long as we're OK with the behavior change, which I consider a bug fix. I think the exhausted state of start/end is unspecified, so nobody should be depending on contains being true then.

@cuviper cuviper added C-bug Category: This is a bug. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Oct 14, 2020
@jonas-schievink jonas-schievink added T-libs Relevant to the library team, which will review and decide on the PR/issue. and removed T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Oct 14, 2020
@Mark-Simulacrum
Copy link
Member

It is a bit unfortunate that this is likely also a regression performance wise, but correctness does seem more important :)

I think we should do this as well, though given the (potential) breakage it would want t-libs FCP.

@SkiFire13
Copy link
Contributor

It should be noted that #78109 doesn't fix <RangeInclusive<T> as RangeBounds<T>>::contains.

Digging deeper it looks like the same problem happens when using the range to index after it has been exhausted (which doesn't use contains). Playground link
The problem is likely the implementation of RangeBounds<T>::end_bound for both RangeInclusive<T> and RangeInclusive<&T> which marks the end bound as inclusive even if the range has been exhausted. This then causes the misbehave in RangeBounds<T>::contains and then RangeInclusive::contains.

@cuviper
Copy link
Member Author

cuviper commented Oct 19, 2020

The problem is likely the implementation of RangeBounds<T>::end_bound for both RangeInclusive<T> and RangeInclusive<&T> which marks the end bound as inclusive even if the range has been exhausted.

That's an interesting thought -- maybe it should return an exclusive end when the iterator is exhausted?

@cuviper
Copy link
Member Author

cuviper commented Oct 19, 2020

Digging deeper it looks like the same problem happens when using the range to index after it has been exhausted (which doesn't use contains). Playground link

This would have to be addressed in SliceIndex<str>/SliceIndex<[T]> for RangeInclusive<usize>.

@bors bors closed this as completed in d9acd7d Oct 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants