As impl Trait
is getting closer to stabilization, I have been wondering about some syntactic sugaring. Perhaps this has been discussed before. If so, please let me know where.
Current state
On current nightly with #[feature(conservative_impl_trait)]
enabled, if I want to write a function that returns an unboxed closure, I can do this:
fn add_number(a: i32) -> impl Fn(i32) -> i32 {
move |b| a + b
}
This is nice, I can provide an implementation without going through all the trouble of (1) defining a new type, (2) implementing the trait for that type and (3) instantiating the type in my function. It all happens in one go.
However, for any other trait, I still have to do all these things:
trait Foo {
fn foo(&self);
}
struct Bar;
impl Foo for Bar {
fn foo(&self) {
println!("the bar way of doing foo!");
}
}
fn some_foo() -> impl Foo {
Bar
}
This is still quite a lot of boilerplate, if all I want is to return the fact that the println!(...)
is to be executed as an implementation of Foo
.
Suggestion
Could we not have something like the following instead?
trait Foo {
fn foo(&self);
}
fn some_foo() -> impl Foo {
|| println!("the bar way of doing foo")
}
I am not quite sure, what to do with traits that have more than one method syntax-wise. One option would be something like this:
trait Foo {
fn foo(&self);
fn bar(self);
}
fn some_foo<'a>(a: i32, b: &'a i32) -> impl Foo + 'a {
impl {
foo: || println!("I am an Fn closure: {}", &b),
bar: move || println!("I am an FnOnce closure: {}", a)
}
}
Ownership seems something to think about, especially as the different closures should actually be able to share the captured state among them, so the whole block just captures all the context once.
I guess the syntax is debatable, it is just the first way of writing it down that came to my mind.
It would make e.g. implementing your own iterables a lot shorter to write.
Do you see any merit in pursuing this idea further? Or is it not worth the extra complexity?