2017-06-03 106 views
0

我想在Rust的頂級函數中使用一個可用的特徵。頂級函數的缺失實現

trait FnTrait { 
    fn call(self); 
} 

impl FnTrait for fn() { 
    fn call(self) { 
     self() 
    } 
} 

fn foo() { 
    println!("Hello, World!") 
} 

fn main() { 
    FnTrait::call(foo) 
} 

但是下面的代碼失敗鑄造foo像這樣

FnTrait::call(foo as fn()) 

與(Playground Link

error[E0277]: the trait bound `fn() {foo}: FnTrait` is not satisfied 
    --> <anon>:16:5 
    | 
16 |  FnTrait::call(foo) 
    |  ^^^^^^^^^^^^^ the trait `FnTrait` is not implemented for `fn() {foo}` 
    | 
    = help: the following implementations were found: 
      <fn() as FnTrait> 
    = note: required by `FnTrait::call` 

編譯我發現我可以欺騙成編譯但它是討厭,我的程序中的一些功能比foo更復雜。任何方法來避免演員?某種程度上我的特質錯了嗎?

回答

3

Rust中的每個函數都有它自己的類型。如您所見,foo不是fn(),它是fn() {foo};可悲的是,這不是你可以在源代碼中編寫的實際類型,這只是一個編譯器消息。區別在於使編譯器更容易讓您將函數作爲值傳遞,同時仍可以內聯該調用。

其結果是命名函數指針不能轉換爲沒有強制轉換或類型提示的通用函數指針。例如,這個工程:

不過,我不知道有任何的方式來利用此獲得性狀工作。

唯一的辦法是停止試圖實施函數指針的特質,而是實現它的一切可調用:(半相關答案約the difference between Fn, FnMut and FnOnce

trait FnTrait { 
    fn call(self); 
} 

impl<F> FnTrait for F where F: FnOnce() { 
    fn call(self) { 
     self() 
    } 
} 

fn foo() { 
    println!("Hello, World!") 
} 

fn main() { 
    foo.call(); 
} 

這將適用於任何可用該簽名調用的任何東西,包括函數和閉包。缺點是你只能有一個這樣的實現。您無法爲其他任何簽名實現此特徵。

一個通用的實現,或許多特定的實現和大量的手動鑄造。選擇你的毒藥。


順便說一句:有作爲鏽「頂級功能」,至少不會像其他種功能不同的事情沒有這樣的事情。功能是功能,不管它們出現在哪裏。實例函數也可以,方法仍然是常規函數,只是它們的第一個參數被稱爲「self」。