0

I have two operators over an Iterator (playground)

fn begins_with<'a, I>(lines: I, query: &'a str) -> impl Iterator<Item = String> + 'a
where
    I: Iterator<Item = String> + 'a,
{
    // Some whatever filtering
    lines.filter(move |line| line.starts_with(&query))
}

fn whatever<'a, I>(lines: I, query: &'a str) -> impl Iterator<Item = String> + 'a
where
    I: Iterator<Item = String> + 'a,
{
    // Some whatever operation
    let mut sorted: Vec<String> = lines.collect();
    sorted.sort_unstable();
    sorted.into_iter().map(|s| s.to_lowercase())
}

I would like to be able to use one or the other and apply them on an iterator of String

One possibility is to use an enum like so (playground):

pub enum Operator {
    BeginsWith,
    Whatever,
}

fn operate<'a, I>(operator: Operator, lines: I, query: &'a str) -> impl Iterator<Item = String> + 'a
where
    I: Iterator<Item = String> + 'a,
{
    match operator {
        Operator::BeginsWith => begins_with(lines, query),
        Operator::Whatever => whatever(lines, query),
    }
}

But I get the following error (which is fair)

error[E0308]: match arms have incompatible types
  --> src/lib.rs:30:31
   |
28 | /     match operator {
29 | |         Operator::BeginsWith => begins_with(lines, query),
   | |                                 ------------------------- this is found to be of type `impl std::iter::Iterator`
30 | |         Operator::Whatever => whatever(lines, query),
   | |                               ^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
31 | |     }
   | |_____- `match` arms have incompatible types
   |
   = note: expected type `impl std::iter::Iterator` (opaque type)
              found type `impl std::iter::Iterator` (opaque type)

Another approach would be to make a trait and have implementations of it (playground):

pub trait Operator {
    pub fn operate<'a, I>(lines: I, query: &'a str) -> impl Iterator<Item = String> + 'a
where
    I: Iterator<Item = String> + 'a;
}

But this is not allowed in Rust.

What mechanisms does Rust provide for me to return an Iterator that depends on the arguments passed into the method implemented from a trait?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
mr.celo
  • 585
  • 1
  • 5
  • 17
  • 1
    You deleted your previous question before I could reply to your comment, but I still believe this is a duplicate of [Conditionally iterate over one of several possible iterators](https://stackoverflow.com/q/29760668/155423). Yes, that question asks specifically about an iterator, but the question and answer can be extended to any trait. – Shepmaster Nov 06 '19 at 16:56
  • [The duplicates applied to your case](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8c191eb1cb008a37cc6f63b71528a799) – Shepmaster Nov 06 '19 at 16:59
  • As already suggested, voting as a duplicate of [Why can impl trait not be used to return multiple / conditional types?](https://stackoverflow.com/questions/52001592/why-can-impl-trait-not-be-used-to-return-multiple-conditional-types) – E_net4 Nov 06 '19 at 16:59
  • See also [Why can impl trait not be used to return multiple / conditional types?](https://stackoverflow.com/q/52001592/155423); [Using impl Trait in Trait definition](https://stackoverflow.com/q/39482131/155423) – Shepmaster Nov 06 '19 at 17:00
  • https://stackoverflow.com/questions/52001592/why-can-impl-trait-not-be-used-to-return-multiple-conditional-types does solve my problem and I will mark this as duplicate. Is a level of indirection the only way to achieve this, then? – mr.celo Nov 06 '19 at 17:06
  • For now, yes. See [Is it possible to use `impl Trait` as a function's return type in a trait definition?](https://stackoverflow.com/a/39490692/155423) for a nightly-only solution that may be what you want. – Shepmaster Nov 06 '19 at 17:08

0 Answers0