Search code examples
methodstypesrustfunction-pointerstraits

How to use a method as a function pointer in Rust


I have a trait method that needs to mutate internal data of a struct and I was hoping I could pass a setter method as a parameter to the trait method. I want to do this so that I can be flexible with what gets mutated by specifying the function passed as a parameter.

This code is a simplification of what I am trying to do, it has the same compiler issue.

Thanks

Code

struct MyStruct {
    i: usize
}

impl MyStruct {
    fn set_i(mut self, i: usize) -> Self {
        self.i = i;
        self
    }
    fn new() -> Self{
        MyStruct{ i: 0 }
    }
}

trait MyTrait {
    fn do_some_setting(&mut self, setter_function: fn(&mut Self, usize) -> Self ) {
        setter_function(&mut self, 7);
    }
}

impl MyTrait for MyStruct {}


fn main() {
    let mut s = MyStruct::new()
                .set_i(3);
                
    assert_eq!(s.i, 3);
    
    s.do_some_setting(MyStruct::set_i);
    
    assert_eq!(s.i, 7);
}

Problem

error[E0308]: mismatched types
  --> src/main.rs:27:23
   |
27 |     s.do_some_setting(MyStruct::set_i);
   |                       ^^^^^^^^^^^^^^^ expected `&mut MyStruct`, found struct `MyStruct`
   |
   = note: expected fn pointer `for<'r> fn(&'r mut MyStruct, _) -> MyStruct`
                 found fn item `fn(MyStruct, _) -> MyStruct {MyStruct::set_i}`

Solution

  • This does not work because MyTrait::do_some_setting expects a function whose first parameter is of type &mut Self whereas the first parameter of MyStruct::set_i is of type mut self.

    You can fix this by changing the signature of MyStruct::set_i to set_i(&mut self, i: usize) but then the compiler would complain about mismatched types as it expects MyStruct::set_i to return Self but you'd be returning &mut Self. You can either derive Clone and return the struct after cloning it, or you can change the return type in the signature to &mut Self.

    The compiler will once again complain about mismatched types as setter_function in MyTrait::do_some_setting is a function that returns Self, not &mut Self. You'll have to change the signature of setter_function to return &mut Self.

    The compiler will now complain that temporary value dropped while borrowed at let mut s = MyStruct::new().set_i(3). You'll have to create MyStruct first and then use set_i on it.

    At last, you'll end up with code that looks as follows:

    struct MyStruct {
        i: usize,
    }
    
    impl MyStruct {
        fn set_i(&mut self, i: usize) -> &mut Self {
            self.i = i;
            self
        }
        fn new() -> Self {
            MyStruct { i: 0 }
        }
    }
    
    trait MyTrait {
        fn do_some_setting(&mut self, setter_function: fn(&mut Self, usize) -> &mut Self) {
            setter_function(self, 7);
        }
    }
    
    impl MyTrait for MyStruct {}
    
    fn main() {
        let mut s = MyStruct::new();
        s.set_i(3);
    
        assert_eq!(s.i, 3);
    
        s.do_some_setting(MyStruct::set_i);
    
        assert_eq!(s.i, 7);
    }
    

    Playground Link