如何从 Rust 中的特征获取函数指针?

How do I get a function pointer from a trait in Rust?

我该如何克服这样的事情:

struct Test {
    foo: Option<fn()>
}

impl Test {
    fn new(&mut self) {
        self.foo = Option::Some(self.a);
    }

    fn a(&self) { /* can use Test */ }
}

我收到这个错误:

error: attempted to take value of method `a` on type `&mut Test`
 --> src/main.rs:7:36
  |
7 |             self.foo = Option::Some(self.a);
  |                                          ^
  |
  = help: maybe a `()` to call it is missing? If not, try an anonymous function

如何从特征传递函数指针?类似于这种情况下发生的情况:

impl Test {
    fn new(&mut self) {
        self.foo = Option::Some(a);
    }
}

fn a() { /* can't use Test */ }

您的代码几乎没有错误。新函数(按照惯例)不应该使用 self 引用,因为它应该创建 Self 类型。

但真正的问题是,Test::foo 需要一个函数类型 fn(),但是 Test::a 的类型是 fn(&Test) == fn a(&self) 如果你将 foo 的类型更改为 fn(&Test) 它将起作用。您还需要使用带有特征名称的函数名称而不是 self。而不是分配给 self.a 你应该分配 Test::a.

这是工作版本:

extern crate chrono;

struct Test {
    foo: Option<fn(&Test)>
}

impl Test {
    fn new() -> Test {
        Test {
            foo: Some(Test::a)
        }
    }

    fn a(&self) {
        println!("a run!");
    }
}

fn main() {
    let test = Test::new();
    test.foo.unwrap()(&test);
}

此外,如果您要在 new() 函数中分配一个字段,并且该值必须始终设置,则无需使用 Option 而是可以像这样:

extern crate chrono;

struct Test {
    foo: fn(&Test)
}

impl Test {
    fn new() -> Test {
        Test {
            foo: Test::a
        }
    }

    fn a(&self) {
        println!("a run!");
    }
}

fn main() {
    let test = Test::new();
    (test.foo)(&test); // Make sure the paranthesis are there
}

你在这里试图做的是从一个函数指针(这里使用 Python 术语,因为 Rust 没有 一个词this) 绑定方法。你不能。

首先,因为 Rust 没有 "bound" 方法的概念;也就是说,您不能引用已经绑定到位的调用者(. 左侧的东西)的方法。如果您想构造一个近似于此的可调用对象,则可以使用闭包; || self.a().

然而,这个仍然不会起作用,因为闭包不是函数指针。没有 "base type" 用于可调用的东西,就像在其他一些语言中一样。函数指针是一种单一的、特定的可调用对象;闭包是完全不同的。相反,有些特征(在实现时)使类型可调用。它们是 FnFnMutFnOnce。因为它们是特征,所以不能将它们用作类型,而必须从某个间接层后面使用它们,例如 Box<FnOnce()>&mut FnMut(i32) -> String.

现在,您 可以 更改 Test 来存储 Option<Box<Fn()>>,但这仍然无济于事。那是因为另一个 other 问题:您试图在自身内部存储对结构的引用。这 不会 会很好地工作。如果你设法做到这一点,你实际上使 Test 值永久无法使用。更有可能的是编译器不会让你走那么远。

Aside: you can do it, but not without resorting to reference counting and dynamic borrow checking, which is out of scope here.

所以你的问题的答案是:你不知道。

让我们改变一下问题:我们可以存储一个根本不会尝试捕获调用者的可调用对象,而不是试图撬开一个自引用闭包。

struct Test {
    foo: Option<Box<Fn(&Test)>>,
}

impl Test {
    fn new() -> Test {
        Test {
            foo: Option::Some(Box::new(Self::a)),
        }
    }

    fn a(&self) { /* can use Test */ }

    fn invoke(&self) {
        if let Some(f) = self.foo.as_ref() {
            f(self);
        }
    }
}

fn main() {
    let t = Test::new();
    t.invoke();
}

被存储的可调用对象现在是一个显式接受调用者的函数,回避了循环引用的问题。我们可以使用它来直接存储 Test::a,方法是将其作为自由函数引用。另请注意,因为 Test 是实现类型,所以我也可以将其称为 Self.

Aside: I've also corrected your Test::new function. Rust doesn't have constructors, just functions that return values like any other.

如果您确信永远不想在 foo 中存储闭包,则可以将 Box<Fn(&Test)> 替换为 fn(&Test)。这将您限制为函数指针,但避免了额外的分配。

如果您还没有阅读,我强烈 强烈建议您阅读the Rust Book