Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trait alias compilation failure when returning impl Alias #67751

Open
hniksic opened this issue Dec 31, 2019 · 0 comments
Open

Trait alias compilation failure when returning impl Alias #67751

hniksic opened this issue Dec 31, 2019 · 0 comments

Comments

@hniksic
Copy link

@hniksic hniksic commented Dec 31, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.