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

时间:2017-06-25 03:48:23

标签: rust function-pointers

我如何克服这样的事情:

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 */ }

2 个答案:

答案 0 :(得分:3)

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

首先,因为Rust没有&#34; bound&#34;方法;也就是说,你不能引用一个方法,其中调用者(.左侧的东西)已经绑定到位。如果你想构造一个近似于它的callable,你可以使用一个闭包; || self.a()

然而,这个仍然不起作用,因为闭包不是函数指针。没有&#34;基本类型&#34;适用于其他语言的可调用内容。函数指针是一种特定的可调用类型;封闭是完全不同的。相反,有些特征(在实现时)使类型可调用。它们是FnFnMutFnOnce。因为它们是特征,所以您不能将它们用作类型,而必须在某些间接层后面使用它们,例如Box<FnOnce()>&mut FnMut(i32) -> String

现在,您可以更改Test来存储Option<Box<Fn()>>,但这仍然无济于事。这是因为另一个其他问题:您正试图在其内部存储对结构的引用。这运行良好。如果您设法执行此操作,则会有效地使Test值永久无法使用。更有可能的是,编译器只是不让你走得那么远。

  

除了:你可以做到这一点,但不是没有借助于引用计数和动态借用检查,这超出了范围。

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

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

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

  

除了:我还更正了您的Test::new功能。 Rust没有构造函数,只是返回值的函数。

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

如果您还没有,我强烈敦促您阅读the Rust Book

答案 1 :(得分:2)

您的代码几乎没有错误。新功能(按惯例)不应该自我引用,因为它应该创建自我类型。

但真正的问题是,Test::foo期待功能类型fn(),但Test::a的类型为fn(&Test) == fn a(&self)如果您更改了它将起作用的foofn(&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
}