Skip to content

HRTB bug in streaming iterator #30786

Closed
@Stebalien

Description

@Stebalien

Full code playpen

So, I've been working on a hacky streaming iterator library and ran into what looks like a bug in the type checker involving higher ranked trait bounds.

In the code below, I've defined a Stream trait. To implement a stream, one implements Stream on &'a mut MyStream for all lifetimes 'a.

pub trait Stream {
    type Item;
    fn next(self) -> Option<Self::Item>;
}

// Example stream
pub struct Repeat(u64);

impl<'a> Stream for &'a mut Repeat {
    type Item = &'a u64;
    fn next(self) -> Option<Self::Item> {
        Some(&self.0)
    }
}

I then went ahead and implemented adapters for filter and map and an extension trait for applying these adapters:

pub struct Map<S, F> {
    stream: S,
    func: F,
}

impl<'a, A, F, T> Stream for &'a mut Map<A, F>
    where &'a mut A: Stream,
          F: FnMut(<&'a mut A as Stream>::Item) -> T,
{
    type Item = T;
    fn next(self) -> Option<T> {
        match self.stream.next() {
            Some(item) => Some((self.func)(item)),
            None => None,
        }
    }
}

pub struct Filter<S, F> {
    stream: S,
    func: F,
}

impl<'a, A, F, T> Stream for &'a mut Filter<A, F>
    where for<'b> &'b mut A: Stream<Item=T>, // <---- BAD
          F: FnMut(&T) -> bool,
{
    type Item = <&'a mut A as Stream>::Item;
    fn next(self) -> Option<Self::Item> {
        while let Some(item) = self.stream.next() {
            if (self.func)(&item) {
                return Some(item);
            }
        }
        None
    }
}

pub trait StreamExt where for<'b> &'b mut Self: Stream {
    fn map<F>(self, func: F) -> Map<Self, F>
        where Self: Sized,
              for<'a> &'a mut Map<Self, F>: Stream,
    {
        Map {
            func: func,
            stream: self,
        }
    }

    fn filter<F>(self, func: F) -> Filter<Self, F>
        where Self: Sized,
              for<'a> &'a mut Filter<Self, F>: Stream,
    {
        Filter {
            func: func,
            stream: self,
        }
    }

    fn count(mut self) -> usize
        where Self: Sized,
    {
        let mut count = 0;
        while let Some(_) = self.next() {
            count += 1;
        }
        count
    }
}

impl<T> StreamExt for T where for<'a> &'a mut T: Stream { }

The problem is the interaction between the Filter and Map adapters. The following works iff the map adapter appears before the filter adapter.

fn main() {
    let source = Repeat(10);
    let map = source.map(|x: &_| x); // Remove to break.
    let filter = map.filter(|x: &_| true);
    let count = filter.count(); // Assert that we still have a valid stream.
}

However, this should never work due to the for<'b> &'b mut A: Stream<Item=T> constraint on the Filter implementation above (marked BAD). Basically, this constraint should assert the stream's Item is the same independent of 'b which should mean that this item doesn't borrow from it's stream (as a matter of fact, I'm using this exact constraint to implement an adapter for converting streams to iterators). However, items returned by both the map and source streams above do obviously borrow from their streams.

Correct error without map:

test.rs:90:12: 90:32 error: type mismatch resolving `for<'b> <&'b mut Repeat as Stream>::Item == _`:
 expected bound lifetime parameter 'b,
    found concrete lifetime [E0271]
test.rs:90           .filter(|l: &_| true)
                      ^~~~~~~~~~~~~~~~~~~~
test.rs:90:12: 90:32 help: run `rustc --explain E0271` to see a detailed explanation
test.rs:91:12: 91:19 error: no method named `count` found for type `Filter<Repeat, [closure@test.rs:90:19: 90:31]>` in the current scope
test.rs:91           .count();
                      ^~~~~~~
test.rs:91:12: 91:19 note: the method `count` exists but the following trait bounds were not satisfied: `&'a mut Filter<Repeat, [closure@test.rs:90:19: 90:31]> : Stream`, `&'a mut &Filter<Repeat, [closure@test.rs:90:19: 90:31]> : Stream`, `&'a mut &mut Filter<Repeat, [closure@test.rs:90:19: 90:31]> : Stream`, `Filter<Repeat, [closure@test.rs:90:19: 90:31]> : core::iter::Iterator`
test.rs:91:12: 91:19 help: items from traits can only be used if the trait is implemented and in scope; the following traits define an item `count`, perhaps you need to implement one of them:
test.rs:91:12: 91:19 help: candidate #1: `StreamExt`
test.rs:91:12: 91:19 help: candidate #2: `core::iter::Iterator`
error: aborting due to 2 previous errors

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions