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

sco*_*t97 2 methods types function-pointers traits 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);
}
Run Code Online (Sandbox Code Playgroud)

问题

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}`
Run Code Online (Sandbox Code Playgroud)

小智 5

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

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

编译器将再次抱怨类型不匹配,因为setter_functionin是一个返回,而不是 的MyTrait::do_some_setting函数。您必须更改 的签名才能返回。Self&mut Selfsetter_function&mut Self

编译器现在会抱怨temporary value dropped while borrowedat let mut s = MyStruct::new().set_i(3)。您必须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);
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接