impl Trait creates ambiguity about whether it is an expression or an item.
It'd work pretty similarly to closures capturing variables, ex:
let x = 1;
// create the closure
let my_closure = move || { println!("{}", x); };
// call the closure
my_closure();
equivalent to:
struct Anonymous {
x: i32,
}
impl Anonymous {
fn call_the_thing(&self) {
println!("{}", self.x);
}
}
let x = 1;
// create the closure
let my_closure = Anonymous { x: x };
// call the closure
my_closure.call_the_thing();
So far we've discussed:
Instantiation Syntax let x = impl Trait { ... };
No let x = impl Trait for struct { var1, var2 } { ... }; or anything similar.
This anonymous impl Trait syntax is meant to be a shorter & simpler version of a regular trait impl with reduced complexity. If any further complexity is needed the user can always just convert it into a conventional item with trait impl.
How to create Self
As the field specifics are left unspecified to the compiler, there wouldn't be a way to construct Self { ... } within the anonymous trait impl. I am aware that this is a restriction; however I'd like to highlight that 1. Closures cannot be constructed in the body of themselves. 2. If you need to construct the anonymous trait impl; you can always make a wrapper function to instantiate the anonymous trait, ex:
fn create_trait(id: u32) -> impl Trait {
let x = impl Trait {
fn id(&self) -> u32 { id }
fn recreate(&self) -> Self { create_trait(id) }
};
x
}
fn do_something(user: User) {
let x = create_trait(user.id);
}
Multiple Trait Bounds (impl Trait1 + impl Trait2)
fn foo() -> impl Trait1 + Trait2 {
return impl { // Is this Trait1 or Trait2?
}
}
Our decision here is to restrict these to be unrepresentable with anonymous traits.
Additionally, traits such as Subtrait: Trait1 + Trait2 couldn't be implemented anonymously due to missing supertraits.
I am very aware that my markdown isn't fancy-looking or anything and I haven't structured this professionally, I can always edit this as we get more refined on what is actually needed and how we'd structure this for an RFC. (Luckily this is a RFC on a RFC lol)
Since Rust is principled about type inference stopping at function boundaries, and the anonymous trait is created inside said boundaries, I say we crank type inference up to the max:
impl instead of impl Trait because we can infer it from the return type. This means that we no longer need to bind it to a variable before returning it.
No type annotations for the trait methods' arguments and return types. These are determined by the trait definition - no need to repeat them.
No self. There is no "self" access, because the data is obtained closure-style. self is for accessing the "parent" value (like self inside a closure)
move is needed for the same reasons it's needed in closures.
If we are uncomfortable with having fns without all the annotations, we can also use struct-like syntax:
I'm a big fan of a lot of this. move impl seems much better. However I'm concerned with it being read as an impl and not impl trait. impl blocks exist on types, and with looking at the body only, it's not obvious to infer that move impl means impl Trait.
IMO I'd prefer move impl trait to fix this, however it is three keywords in a row and I'm also not a huge fan of that.
I think we need to cover self as a valid parameter. Interop with existing code and using anonymous trait impls becomes difficult if they supply a &self vs a &mut self parameter; or none at all. What we are allowed to do with this type and it's captures becomes different depending on the &self and &mut self parameter.
My preferred syntax is struct impl TraitName { .... }. This avoids any syntactic ambiguity since the token sequence struct impl is currently not in the language. And I think that someone who hasn't seen this syntax before is more likely to understand the meaning of this syntax than with other syntaxes.
I just updated the post contents; both cases would be restricted. The first wouldn't be allowed because it is not a singular trait bound. The second would not be allowed because an impl would be missing supertraits A and B.