Skip to content

Trait alias compilation failure when returning impl Alias #67751

@hniksic

Description

@hniksic

While working on a stackoverflow question I encountered the need for trait aliases. For example, this code (playground) uses a very long trait several times:

fn to_box_factory<F, RET>(f: F) -> impl Fn(&'static str) -> Box<dyn Iterator<Item = &'static str>>
where
    F: Fn(&'static str) -> RET,
    RET: Iterator<Item = &'static str> + 'static,
{
    move |s| Box::new(f(s))
}

fn main() {
    let f1: &dyn Fn(&'static str) -> Box<dyn Iterator<Item = &'static str>> = &to_box_factory(|s| s.split_whitespace());
    let f2: &dyn Fn(&'static str) -> Box<dyn Iterator<Item = &'static str>> = &to_box_factory(|s| s.split_ascii_whitespace());
    let fs = vec![f1, f2];
    fs[0]("rust 2020").for_each(|s| println!("{}", s));
    fs[1]("rust 2020").for_each(|s| println!("{}", s));
}

It would improve readability and reduce duplication to introduce a trait alias for Fn(&'static str) -> Box<dyn Iterator<Item = &'static str>>. My attempt was to simply replace all uses of the trait with the alias, resulting in the following (playground):

#![feature(trait_alias)]
trait BoxFactory = Fn(&'static str) -> Box<dyn Iterator<Item = &'static str>>;

fn to_box_factory<F, RET>(f: F) -> impl BoxFactory
where
    F: Fn(&'static str) -> RET,
    RET: Iterator<Item = &'static str> + 'static,
{
    move |s| Box::new(f(s))
}

fn main() {
    let f1: &dyn BoxFactory = &to_box_factory(|s| s.split_whitespace());
    let f2: &dyn BoxFactory = &to_box_factory(|s| s.split_ascii_whitespace());
    let fs = vec![f1, f2];
    fs[0]("rust 2020").for_each(|s| println!("{}", s));
    fs[1]("rust 2020").for_each(|s| println!("{}", s));
}

However, that fails to compile with the following error:

error[E0271]: type mismatch resolving `<[closure@src/main.rs:9:5: 9:28 f:_] as std::ops::FnOnce<(&'static str,)>>::Output == std::boxed::Box<(dyn std::iter::Iterator<Item = &'static str> + 'static)>`
 --> src/main.rs:4:36
  |
4 | fn to_box_factory<F, RET>(f: F) -> impl BoxFactory
  |                      ---           ^^^^^^^^^^^^^^^ expected type parameter `RET`, found trait `std::iter::Iterator`
  |                      |
  |                      this type parameter
  |
  = note: expected struct `std::boxed::Box<RET>`
             found struct `std::boxed::Box<(dyn std::iter::Iterator<Item = &'static str> + 'static)>`
  = help: type parameters must be constrained to match other types
  = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
  = note: the return type of a function must have a statically known size

I'm not sure I understand that error, nor do I understand why the error doesn't appear in the first version.

Curiously, if I just replace -> impl BoxFactory with the full incantation of -> impl Fn(&'static str) -> Box<dyn Iterator<Item = &'static str>> (playground), but leave the other two uses of the alias in main(), the code compiles and runs correctly. Is it a bug that returning impl Alias fails, while returning impl [original trait] works?

Note: I am aware that trait aliases are experimental. My motivation for reporting this issue is to ensure that problems with the feature (if any) are worked out before it is stabilized.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.C-bugCategory: This is a bug.F-trait_alias`#![feature(trait_alias)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions