Inferring trait method return types

I've got a trait which looks like this:

pub trait ViewFactory {
    type View: View;

    fn create(&self) -> Self::View;
}

However, what I really want is this:

pub trait ViewFactory {
    fn create(&self) -> impl View;
}

The reason I want this is because create() typically returns an expression with a very complicated type signature such as Fragment<(Cond<String, String>, Element, Element, String, ForEach<Item, String>)> and so on. It's very unergonomic to force everyone who wants to implement ViewFactory to have to figure out the exact type that is produced by the expression.

Unfortunately, in order to cache the result from ViewFactory I also need to know the return type. Here's a typical example of how a ViewFactory gets used:

impl<Factory: ViewFactory> FactoryOutput for Factory {
    type State = (Factory::View, <Factory::View as View>::State);

    fn build(&self, cx: &mut Cx) -> Self::State {
        let view = self.create(cx);
        let state = view.build(cx);
        (view, state)
    }

    fn rebuild(&self, cx: &mut Cx, &mut state: Self::State) {
        state.0 = self.create(cx);
        let state = view.rebuild(cx, &state.1);
    }
}

In other words, the associated type needs to know the exact type returned by create() so that it can create a tuple containing the output of create().

If create was a plain function instead of a trait method, I might be able to infer the return type using some fancy generics. But there doesn't seem to be a way to do this with a method.

Type erasing the result of create() is problematic because I'd need to box the result, which means an additional memory allocation. The call to create() is meant to be cheap, the view is a temporary value which is constructed and thrown away immediately.

A real solution to that is beyond me, but I wonder if a practical workaround is to provide one more more type aliases, which can be parameterized for specific cases, to make it easier to create the View types.

It seems you don't really need to

know the exact type returned by create()

so long as it has some nameable path like

Factory::View

in which case you probably want one of

The first will probably land in some form before the second, if I had to guess.

I don't know of any way to emulate return type notation on stable, since function item types are unnameable.


In the meanwhile, though unergonomic, it sounds like the expected types are nameable, so at least it's not unsurmountable (return a mismatched type and the compiler will probably tell you the answer).

2 Likes

Thanks. Either of those RFCs would solve my problem, as you pointed out. Given however that the first one has been open since 2019, I'm not holding my breath.

For the workaround you suggest, the expected types might not be nameable: some view types, like Memoize, take a closure as an argument.

We have async fn and return position impl Trait in traits now, so things are moving.

With unnameable types, in the meanwhile, type erasure or partial type erasure (e.g. function pointers or box up the closures) is still the only stable solution when you must name the types, as far as I'm aware.