I have been reading the various RFCs/comments on existential types and TAITs and trying it out a bit on nightly. To be honest even though the feature is undoubtedly powerful, I'm not super fond of it due to the type inference complexity where the actual type is determined over modules or maybe even the whole crate.
It feels like TAITs are going to create an implicit, hard-to-follow but at the same time fairly tight relationship between the declaration site and the definition site.
At the same time, proposal is being made to have the ability to name existential return types and it seems like this will be needed anyway in order to be able to express Send
bounds and the like.
It occurs to me having RTN aliases instead of TAITs would achieve more or less the same (?) without having to go through all the complexity of defining the right inference rules and with much improved clarity of the code - I could just follow from the alias declaration site directly to where the type is defined (I could navigate there using an IDE easily). By "RTN aliases" I mean roughly something like this, using syntax from Niko's blogpost:
pub type AnAlias = bar();
fn bar() -> impl Iterator {
// ...
}
pub type AnotherAlias<T> = baz::<T>(..);
fn bar<T>(x: T) -> impl Iterator<Item = T> {
// ...
}
I guess the crucial question here is whether TAITs / existential types can do things that an RTN alias couldn't and whether that extra power justifies the added complexity and ergonomics burden.
It seems that most of the time TAITs would be used to spec a return type of a specific async- or closure-featuring function. One thing that comes to mind is lifetime/types capture specs, ie. opting out of the default capture-everything behaviour, but it seems this could be solved with the -> impl<'a, T>
syntax without putting distance between the function and the spot where these rules are defined - which is also an uncomfortable feature of TAITs.
Wdyt? Thanks!