2
votes

I'm trying to create a trait that extends iterator from strings and return a flat map, here is what I got so far

trait WordsExt<'a, F>: Iterator<Item = &'a str>
where
    Self: Sized,
    F: FnMut(&'a str) -> Split<'a, &'a str>,
{
    fn words(self) -> FlatMap<Self, Split<'a, &'a str>, F> {
        self.flat_map(|x: &'a str| x.split(" "))
    }
}

I'm stuck on closure type.

I tried this too

struct Words<'a, I>
where
    I: Iterator<Item = &'a str>,
{
    inner: I,
}

trait WordsExt<'a>: Iterator<Item = &'a str>
where
    Self: Sized,
{
    fn words(
        self,
    ) -> Words<'a, FlatMap<Self, Split<'a, &'a str>, Fn(Self::Item) -> Split<'a, &'a str>>>
    {
        Words {
            inner: self.flat_map(|x: &'a str| x.split(" ")),
        }
    }
}

I just came across this problem every time. I need to return a trait. When I'm in a function I can use impl Trait syntax. But when expressing a trait I can't express this. From what I could grasp, the closer I can get is to use generics, so that the method is monomorphized at call. But then I need to enable user to select the concrete type. In this case the closure is implementation detail, it should not leak to user.

I came across this answer How can I add new methods to Iterator?

In this case the OP use case has an inner state. In my case I just want to create an alias i.words() -> i.flat_map(|x| x.split(" "))

I know about coherence, so that I may need to create a wrapper over Iterator because Iterator is not under my crate. This was the second attempt.

I could implement a function by the way it was straightforward

fn words<'a>(i: impl Iterator<Item = &'a str>) -> impl Iterator<Item = &'a str> {
    i.flat_map(|x| x.split(" "))
}

But I can't express the same with trait syntax because there is no impl Trait syntax in traits. So ... I have two options, dynamic dispatch and generics, none of them are ideal. Going even further. Since these struct is unsized I can't use static dispatch on it, so I'm stuck on dynamic dispatch on something that would be simply simple

Invalid struct bellow

struct Words<'a>(Iterator<Item = &'a str>);

Finally, I expected that given a function F(X) I would be able to always refactor it to write a X.F(), but this seems not to be true because while I'm able to write fn f(x) -> impl Y I'm not able to express trait X { fn (self) -> impl Y }, at last not recurring to generics or dynamic dispatch.

I was trying to write iterator combinators in a kind of fluent syntax: iterator.comb1().comb2().comb3()

1

1 Answers

2
votes

I got your code to compile by changing it as follows:

trait WordsExt<'a>: Iterator<Item = &'a str>
where
  Self: Sized,
{
  fn words(self) -> FlatMap<Self, Split<'a, &'a str>, fn(&'a str) -> Split<'a, &'a str>> {
    self.flat_map(|x: &'a str| x.split(" "))
  }
}

The trick is to replace the function trait object by the actual function type.