簡體   English   中英

如何從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 */ }

什么你想要做的,是從(這里使用Python術語,因為銹沒有一個詞來形容)綁定的方法獲得一個函數指針。 你不能

首先,因為Rust沒有“綁定”方法的概念; 也就是說,你不能引用與調用者(在其左邊的事情的方法. )已經到位的約束。 如果您想構造一個與此近似的可調用對象,則可以使用閉包; || self.a() || self.a()

但是,由於閉包不是函數指針 ,因此這仍然行不通。 像某些其他語言一樣,沒有可調用事物的“基本類型”。 函數指針是一種特定的可調用類型。 閉包是完全不同的。 相反,有些特征(在實現時)使類型可調用。 它們是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) 這限制了您使用函數指針,但是避免了額外的分配。

如果您還沒有的話,我強烈建議您閱讀Rust Book

您的代碼幾乎沒有錯誤。 新函數(按照慣例)不應采用自我引用,因為它應該創建自我類型。

但是真正的問題是, Test::foo期望函數類型為fn() ,但是如果將foo的類型更改為fn(&Test) fn() ,則Test::a的類型為fn(&Test) == fn a(&self) 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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM