4
votes

I have an extension trait whose methods are just shorthands for adapters/combinators:

fn foo(self) -> ... { self.map(|i| i * 2).foo().bar() }

The return type of Trait::foo() is some nested Map<Foo<Bar<Filter..., including closures, and is therefor anonymous for all practical purposes. My problem is how to return such a type from a trait method, preferably without using Box.

  • impl Trait in return position would be the way to go, yet this feature is not implemented for trait methods yet.
  • Returning a Box<Trait>is possible, yet I don't want to allocate for every adapter shorthanded by the trait.
  • I can't put the anonymous type into a struct and return that, because struct Foo<T> { inner: T } can't be implemented (I promise an impl for all T, yet only return a specific Foo<Map<Filter<Bar...).
  • Existential types would probably solve the above problem, yet they won't be implemented for some time.

I could also just avoid the problem and use a macro or a freestanding function; this also feels unhygienic, though.

Any more insights?

1

1 Answers

3
votes

What is the correct way to return an Iterator (or any other trait)? covers all the present solutions. The one you haven't used is to replace closures with function pointers and then use a type alias (optionally wrapping in a newtype). This isn't always possible, but since you didn't provide a MCVE of your code, we can't tell if this will work for you or not:

use std::iter;

type Thing<T> = iter::Map<iter::Filter<T, fn(&i32) -> bool>, fn(i32) -> i32>;

trait IterExt: Iterator<Item = i32> {
    fn thing(self) -> Thing<Self>
    where
        Self: Sized + 'static,
    {
        // self.filter(|&v| v > 10).map(|v| v * 2)
        fn a(v: &i32) -> bool { *v > 10 }
        fn b(v: i32) -> i32 { v * 2 }
        self.filter(a as fn(&i32) -> bool).map(b as fn(i32) -> i32)
    }
}

impl<I> IterExt for I
where
    I: Iterator<Item = i32>,
{}

fn main() {}

Honestly, in these cases I would create a newtype wrapping the boxed trait object. That way, I have the flexibility to internally re-implement it with a non-boxed option in an API-compatible fashion when it becomes practical to do so.