Idea: Anonymous `impl` types that support capturing like closures

I'm using the listener patterns heavily, and I hope there are anonymous impl types that support capturing like closures. When people are defining a listener, they have to define a struct and implement the trait for it. But since the type is used only once. using anonymous impl type can reduce the code to write and make it clearer.

I know that it is a bad idea to use anonymous types in library code. But anonymous types (including closures) can help the client codes or test codes easier to write.

Trait definition:

trait Person {
    fn name(&self) -> &str;
    fn set_name(&mut self, name: String);
    fn hello();
    fn pass_away(self);

fn life<P: Person>(mut person: P) {
    println!("name: {}",;
    println!("new name: {}",;


let mut name = String::new("Alice");
life(move impl Person for ? {
    fn name => || &name,
    fn set_name => |new_name| name = new_name,
    fn hello => || println!("Hello"),
    // Alternative:
    // fn hello() { println!("Hello") }
    fn pass_away => || println!("Goodbye"),

Desugars to

struct __ImplPerson {
    name: String,
impl Person for __ImplPerson {
    fn name(&self) -> &str { & }
    fn set_name(&mut self, new_name: String) { = new_name; }
    fn hello() { println!("Hello") }
    fn pass_away(self) { println!("Goodbye") }

let name = String::new("Alice");
life(__ImplPerson { name });
1 Like

This seems more of an ad-hoc solution that just so happens to fit your use case rather than something that would necessarily be useful for the Rust ecosystem. At least, the latter hasn't been shown.

Rather than adding compiler support for this, I think this is better solved by using proc_macros. You already provided the desired expansion and syntax, so it shouldn't be too hard to write.

The problem is not only that proc_marcos don't know which paths to capture, and whether capturing by value, by mutable borrow or by immutable borrow, but also they don't know the types of the captured value so they can't construct such an anonymous struct.


A fairly obvious solution to that is to provide the closures as inputs to the macro. Doing so provides users of the macro with full control over anything any of the closure capture.

They are not separate closures though, but share a single set of (possibly owned or mutable) captures.

It's of course possible I'm missing something, but mutable borrows can't be shared between closures, nor can owned values, at least not without putting them behind a mutex or something like that. Immutable borrows can of course be shared, so that wouldn't be a problem.

All of this is true regardless of whether there's a macro at play or not. Coupled with the fact that none of these fundamental rules are about to be changed, it suggests to me that a redesign may be necessary to make such sharing possible.

Right, that’s the main reason why there are not multiple separate closures here, but a single object that captures local variables and implements a trait with multiple methods.

OP chose some closure-like syntax here, but that syntax is just arbitrary syntax, not referring to actual closures being involved; the meaning is supposed to be more like the desugaring OP provided, too.

1 Like

Indeed. So why not put the stuff that the various "closures" need to access in a struct, and then add an impl block for the struct with methods that take the right sort of receiver for their respective purpose? (potentially important caveat: I'm assuming that the struct can own all of that data. View structs might complicate matters.)

I suppose you mean => || &name, here, right?

Yeah, I always wondered why anyone ever needed (ordinary) closures, either. I’d much rather write

fn foo(a: i32) {
    let it = [1, 2, 3].iter().map({
        struct Closure<'a> {
            a: &'a i32,
        impl<'a> FnOnce<(&'a i32,)> for Closure<'_> {
            type Output = i32;
            extern "rust-call" fn call_once(mut self, args: (&'a i32,)) -> i32 {
        impl<'a> FnMut<(&'a i32,)> for Closure<'_> {
            extern "rust-call" fn call_mut(&mut self, args: (&'a i32,)) -> i32 {
                *args.0 + *self.a
        Closure {
            a: &a,

instead of

fn foo(a: i32) {
    let it = [1, 2, 3].iter().map(|x| *x + a);

I mean, the former is just so much more straightforward, no magically compiler-implemented Fn* traits, no implicit capture rules to learn, much clearer.

Edit: Sorry for the sarcastic response, the point is: it’s shorter to write. And that’s particularly relevant since it’s something that you can’t do with macros instead because a macro can’t determine the set of captured variables, the capture modes, and their types. At the end of the day, ordinary closures could perhaps just become (mostly) syntactic sugar for an anonymous impl Fn, impl FnMut, or impl FnOnce.


Yes, it's a typo.

I used the closure syntax because:

  1. I need to define the functions of the trait;
  2. There are already two syntaxes in Rust to define a function: the fn item and the closure expr;
  3. ordinary functions don't capture values from the context, so the closure style is more appropriate here;
  4. The associated function is an exception: it doesn't capture any external values at all (and it is not able to), so I use the fn item as an alternative syntax.

The multiple closures together look like a match expression because:

  1. There might be multiple functions to define, so the closures should be named;
  2. Closures don't always include a braced block, when they contains a single expression, so there should be a delimiter which separates them apart; Candidates are ; and ,;
  3. ; may not be a good idea because it cannot distinguish whether the expression is an expression which returns some value, or just a statement that returns ().
  4. There are multiple closures in a braced blocks, delimited by ,, and need different names to distinguish which closure is the definition of which trait method. It let me think of the match expression. Finally, the syntax looks like what I've written before.

There is another syntax that might be less confusing to avoid the closure syntax:

let mut name = String::new("Alice");
life(impl Person for struct { name } {
    fn name(&self) { & }
    fn set_name(&mut self, new_name: String) { = new_name }
    fn hello() { println!("Hello") }
    fn pass_away(self) { println!("Goodbye") }

Where struct { name } is an anonymous struct expression. You can also write struct { name: &name } if you want to capture by reference.

1 Like

FWIW, I don’t like the syntax you propose, but the precise syntax is also not the most important point about this kind of feature.

Despite your sarcastic tone, I won't answer in kind.

That wasn't the point of my previous response. I'm not arguing from a Blub programmer perspective (made famous by the Blub paradox).

My point was that Rust has arguably already exceeded its complexity and novelty budgets. The answers provided by the community to the last couple of yearly Rust surveys suggest as much. It's dangerously close to becoming C++-like in that different projects will go ahead and use different subparts of the language, and if this goes far enough, they will become mutually unintelligible i.e. It will fracture the ecosystem.

The big challenge there is the boiling frog effect: every single time it's "just one more feature because reasons". But before you know it that frog is good and well cooked.

So what my default position has become could be summarized as: "if it's already possible in Rust, just go ahead and use that. Especially if the main effect of a potential feature is to save some key strokes."


Actually I don't like it either. I just feel it is too tired (of me and the user of my library) to define lots of temporary structs, impl the listener trait for them and construct with a struct expression.

BTW, I'm implementing a data transportation protocol, where the listeners are defined by the protocol's specification, and I can't touch it.

1 Like

Sure, I agree with you. So the tag for this topic is just "idea". If it is not possible in current Rust, I might rather consider to write an (pre)-RFC (if I really need it) instead of to post an idea. Suppose I'm a team member of Rust, I cannot accept this feature either. I'm just here to seek if someone has better, and possibly more feasible ideas.

I submitted a half-assed RFC for this a while back: RFC: impl trait expressions by canndrew · Pull Request #2604 · rust-lang/rfcs · GitHub


Cool! Seems that this RFC is postponed and might be revisited in the future?

An observation on prior art (not a criticism): This proposed feature seems nearly identical to Java's anonymous class feature.

The syntax seems not ideal, as a weird hybrid of closure and fn syntax, but I can see that unmarked fn syntax would have an overall understandability cost since it would mean the language no longer obeys the rule "fns never capture".

I think this could be useful for writing some kinds of tests — I often find myself writing one-off structs and trait impls for single tests.

On the other hand, perhaps those would be best supported by (rather than new language features) the trait's author providing helpers that take closure inputs, in the way that std::iter::from_fn lets you effectively impl Iterator without writing out the type and impl. (That's harder in cases where the trait has multiple methods sharing mutable state, though.)


I mean, theoretically. There's a loong back log of postponed RFCs though, most of which have no real chance of ever making it into the language. If you want to push for this your best bet would probably be to improve the RFC (I'll merge any decent pull requests to my pull request branch) then take the discussion over to zulip and ask if it can be re-considered. I think your chances are slim though. There's already a back log of accepted RFCs which still need to be implemented. And the devs are pretty wary of approving entirely new features at this point.

1 Like

Agreed. I would encourage not reading much into the distinction between closed & postponed for RFCs.

(I didn't look at the RFC in question, but) As a general note, one way something can get postponed is with a "we might be interested in doing something to address this pain point", which can happen even if the specific design isn't something promising itself.