如何在 Rust 中将方法用作函数指针

How to use a method as a function pointer in Rust

我有一个特征方法需要改变结构的内部数据,我希望我可以将 setter 方法作为参数传递给特征方法。我想这样做,以便我可以通过指定作为参数传递的函数来灵活地处理变异的内容。

此代码是对我正在尝试做的事情的简化,它具有相同的编译器问题。

谢谢

代码

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);
}

问题

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}`

这不起作用,因为 MyTrait::do_some_setting 需要第一个参数为 &mut Self 类型的函数,而 MyStruct::set_i 的第一个参数为 mut self 类型。

您可以通过将 MyStruct::set_i 的签名更改为 set_i(&mut self, i: usize) 来解决此问题,但是编译器会抱怨 mismatched types 因为它期望 MyStruct::set_i 为 return Self 但你会 returning &mut Self。您可以在克隆后派生 Clone 和 return 结构,也可以将签名中的 return 类型更改为 &mut Self.

编译器将再次抱怨类型不匹配,因为 MyTrait::do_some_setting 中的 setter_function 是 returns Self 的函数,而不是 &mut Self。您必须将 setter_function 的签名更改为 return &mut Self.

编译器现在会在 let mut s = MyStruct::new().set_i(3) 处抱怨 temporary value dropped while borrowed。您必须先创建 MyStruct,然后在其上使用 set_i

最后,您将得到如下代码:

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