Instance method as sugar for closure

A pattern I've noticed a few times is wrapping an instance method as a closure, eg as explained in How can I pass a struct method for a closure. This seems to be especially useful in iterator chaining when you need to pass in lots of functions: something like .filter(|x| set.contains(x)), where set is say of type HashSet.

As a casual rust user, I expected .filter(set.contains) to work too. Since it doesn't work that way, I was wondering if there's a good reason for instance.method to not be syntactic sugar for the closure |x| instance.method(x). Maybe there's a case where instance.method already means something as an expression and would cause ambiguity? Or is it just about explicitness?

One place where I could see an issue is if a method shares its name with a field and the field is also a function type, but the syntax to call that field is already a bit awkward in this case (requiring parentheses that look redundant but are not) and doesn't seem like a common scenario.

I would guess that this has been brought up before but I can only find discussions that tell you how to write the correct thing, like the link above, not why the wrong thing is bad.

It still is an issue if the field is not a function type. For builder types it's pretty common and intuitive.

1 Like

I'm not sure what you mean. I'm aware that it's common for methods and fields to share names, but if only one of them makes sense at a place a function type is expected then there is no ambiguity.

In the presence of generics and traits, it is important to know which two things you're type matching before you use any type information from them. Methods are an intentional exception to this — method receiver types can't be determined from method call return types — but we can't backwards-compatibly introduce more such exceptions.

The following silly program demonstrates that adding this syntax would add an unresolvable ambiguity to existing code:

trait Tr {
    fn check(self) -> &'static str;
}
impl<T: Fn() -> i32> Tr for T {
    fn check(self) -> &'static str {
        "function"
    }
}
impl Tr for bool {
    fn check(self) -> &'static str {
        "bool"
    }
}

struct Example {
    foo: bool,
}
impl Example {
    fn foo(&self) -> i32 {
        self.foo as i32
    }
}

fn main() {
    println!("{}", Tr::check(Example { foo: true }.foo)); // not a function!
    println!("{}", Tr::check(|| Example { foo: true }.foo()));
}

Implicit method closures could be added with a different syntax, but they can't be added with plain dot syntax (unless it only works when there is no visible field, which would be an additional complication and another way to break code at a distance).

4 Likes