- 
                Notifications
    You must be signed in to change notification settings 
- Fork 1.6k
Description
Right now, you can do this:
fn foo() -> impl Trait {
    ...   // the return value must implement Trait
}In this issue I suggest that we add this syntax as well:
type FooRet = impl Trait;
fn foo() -> FooRet {
    ...   // the return value must implement Trait
}This means that later we can use FooRet to designate the type that foo() returns.
This makes it possible to put it in a struct for example:
struct Bar {
    foo: FooRet,
}
impl Bar {
    fn new() -> Bar {
        Bar { foo: foo() }
    }
}Unresolved questions
- Do we allow multiple functions to return the same "type=impl type" if their effective return type is the same?
- Handling template parameters looks straight-forward (type Foo<R> = impl Trait; fn foo<R>() -> Foo<R>), but I'm not totally sure that there's a not problem with that.
Alternatives
- Add a syntax that resolves the return type of a function, similar to C++'s decltype.
Motivation
While the feature itself is -I think- mostly straight-forward, the biggest debate is probably the motivation.
Let's take this code:
struct MyIterator { ... }
impl Iterator for MyIterator { ... }
fn get_my_iterator() -> MyIterator { ... }Right now you can wrap around it, if you want:
struct Wrapper(MyIterator);
fn get_wrapper() -> Wrapper { Wrapper(get_my_iterator() }This is a zero-cost abstraction and a good usage of composition.
The problem begins when you want to use -> impl Trait:
fn get_my_iterator() -> impl Iterator { ... }Suddenly, wrapping around it becomes hacky. The primary way to do so is this:
struct Wrapper<I>(I) where I: Iterator;
fn get_wrapper() -> Wrapper<impl Iterator> { Wrapper(get_my_iterator()) }(The alternative way is to create a new trait named TraitForWrapper which contains the API of Wrapper, and make get_wrapper() return -> impl TraitForWrapper)
This not only complicates the documentation and makes the usage of get_wrapper() more confusing for beginners, but it is also leaky, as it makes wrapping around Wrapper more complicated:
struct WrapperWrapper<I>(Wrapper<I>) where I: Iterator;
fn get_wrapper_wrapper() -> WrapperWrapper<impl Iterator> { WrapperWrapper(get_wrapper()) }(using the TraitForWrapper method is not better)
This looks like an hypothetical example, but for example if I were to use -> impl Trait in my project, some equivalents to WrapperWrapper would look nightmarish:
struct RenderSystem<Rp1, Rp2, Rp3, Pl1, Pl2, Pl3> {
    fog_of_war_pipeline: GraphicsPipeline<SimpleVertexDefinition, Pl1, Rp1>,
    background_pipeline: GraphicsPipeline<SimpleVertexDefinition, Pl2, Rp2>,
    lighting_pipeline: GraphicsPipeline<SimpleVertexDefinition, Pl3, Rp3>,
}Basically, the problem of the -> impl Trait syntax is that it's poisonous. Once you use it in an API, all the codes that use this API will have to use -> impl Trait as well.
This proposition would fix this problem:
type MyIterator = impl Iterator;
fn get_my_iterator() -> MyIterator { ... }
struct Wrapper(MyIterator);
fn get_wrapper() -> Wrapper { Wrapper(get_my_iterator()) }
struct WrapperWrapper(Wrapper);
fn get_wrapper_wrapper() -> WrapperWrapper { WrapperWrapper(get_wrapper()) }