Closure on structs usability suggestion

consider the following code

fn run<F>(f: F) where F: Fn() {
    f();
}

struct A <F: Fn()> {
    f: F
}

fn main() {
    let p = || println!("hello world");
    let a = A{f: p};

    // works
    p();

    // works
    run(a.f);

    // does not work
    a.f();
}

p is a closure that can is set on struct A. you can't call a.f() as it tells you that it must be a method. you have to wrap a.f in brackets (a.f)().

if you consider the run function it just uses the closer as a function and runs it. I feel that the brackets around a.f should not be a required syntax. The compiler (not that i know how to write compilers) should be clever enough to realize that you set a closure on the struct and thus it behaves like one.

if I can call p(); I should be able to use a.f().

The compiler is aware of this case and outputs a nice help message when it occurs:

error[E0599]: no method named `f` found for struct `A<[closure@src/main.rs:6:20: 6:46]>` in the current scope
 --> src/main.rs:7:7
  |
1 | struct A <F: Fn()> {
  | ------------------ method `f` not found for this
...
7 |     a.f();
  |       ^ field, not a method
  |
help: to call the function stored in `f`, surround the field access with parentheses
  |
7 |     (a.f)();
  |     ^   ^

If you want to remove the extra parentheses requirement, what happens when a method exists with the same name?

struct A <F: Fn()> {
    f: F
}

impl<F: Fn()> A<F> {
    fn f(&self) {
        println!("hello from method")
    }
}

fn main() {
    let a = A { f: || println!("hello from field") };
    a.f(); // => "hello from method"
}

The extra parentheses are required because of the ambiguity, and I don't see any way around it.

9 Likes

In fairness a similar kind of ambiguity may exist between

  • same name methods of different in-scope traits (invocation of such a method does not compile)
  • an inherent and trait methods (the inherent method overshadows the trait one)

Coming purely from a place of logic there are at least two possible ways around it, as noted above. I'm not arguing for any of them, just pointing out that these ways around do exist.

1 Like