2017-02-15 50 views
0

我想調用函數依賴於外部數據, 這樣的:是否可以使用特徵作爲免費函數的語法糖?

struct Foo { 
    data: &'static str, 
    handler: Option<fn (i32) -> String>, 
} 

fn aaa_converter(_: i32) -> String { unimplemented!(); } 
fn bbb_converter(_: i32) -> String { unimplemented!(); } 

fn main() { 
    let _ = Foo{data: "aaa", handler: Some(aaa_converter)}; 
    let _ = Foo{data: "bbb", handler: Some(bbb_converter)}; 
    let _ = Foo{data: "ccc", handler: None}; 
} 

我作爲輸入字符串「AAA」,我需要調用aaa_converter。所有工作正常,我把Foo對象放入哈希映射中,如果不是None,則調用合適的handler

現在我有很多這樣的轉換器,我想從語言的幫助來處理它們。

理想情況下,將語法如下:

trait Handler { 
    fn handle(a: i32) -> String; 
} 

impl Handler for "aaa" { 
    // ... 
} 

,我能得到的最好的比賽是:

trait Handler { 
    fn handle(/*&self, */a: i32) -> String; 
} 

struct aaa; 

impl Handler for aaa { 
    fn handle(/*&self, */a: i32) -> String { 
     unimplemented!(); 
    } 
} 

struct Foo { 
    data: &'static str, 
    handler: &'static Handler, 
} 

fn main() {} 

但這樣的代碼不能編譯:

the trait `Handler` cannot be made into an object 
    = note: method `handle` has no receiver 

How to call a trait method without a struct instance?看起來相關,但答案中的RFC已過時。從那以後,這種語言也可能發生了一些變化?

是否可以使用特徵作爲指向免費函數的簡單指針?

或者還有另一種方法來組織處理程序?

+1

*爲什麼*?爲什麼不只是有一個空的結構,採取'self'參數,而不是使用它? – Shepmaster

+1

@Shepmaster:爲什麼要用特質開始? ;) –

+0

@MatthieuM。我的精神感覺告訴我,有時他們會想要狀態。例如,'Foo'可能是實際的處理程序,'data'應該通過'self'來訪問。 – Shepmaster

回答

1

有一個誤區,在這裏:

  1. 鏽病具有以下功能:
  2. 鏽有 「仿函數」
  3. 鏽病性狀

一個功能就是:

fn i_am_a_function(a: i32) -> String { a.to_string() } 

仿函子是一個函數ob ject,這是與某個狀態相關的功能。拉斯特實際上有其中3:

FnOnce(i32) -> String 
FnMut(i32) -> String 
Fn(i32) -> String 

最後鏽病性狀:

trait Handler { 
    fn non_object_safe(a: i32) -> String; 
    fn object_safe(&self, a: i32) -> String; 
} 

性狀可以在兩種情況下使用:

  • 在通用功能提供界
  • 當物體安全,提供類型擦除

粗略地講,性狀是不是對象安全的,如果任何的其相關聯的功能:

  • 不具有&self&mut self參數
  • 提到Self(類型)

對於有關這兩個概念的更多信息,請查看Rust Book。


在你的情況,你可以使用:

  1. 一個簡單的函數指針:fn(i32) -> String
  2. 算符Fn(i32) -> String
  3. 對象的安全特徵

唯一你不能使用是一個非對象安全的特性,當然,由於墨菲,這是唯一的選擇你選了。

在你的情況下,最簡單的解決方法是使用一個對象的安全特性:

trait Handler { 
    fn handle(&self, a: i32) -> String; 
} 

struct A; 

impl Handler for A { 
    fn handle(&self, a: i32) -> String { 
     a.to_string() 
    } 
} 

const STATIC_A: &'static Handler = &A; 

struct Foo { 
    data: &'static str, 
    handler: &'static Handler, 
} 

fn main() { 
    let foo = Foo { data: "aaa", handler: STATIC_A }; 
    println!("{}", foo.handler.handle(3)); 
} 

如果該數據指針是64位的開銷真的打擾你,那麼你可以使用函數指針和建立自己的虛擬桌面:

struct Handler { 
    handle: fn(i32) -> String, 
} 

fn aaa(a: i32) -> String { 
    a.to_string() 
} 

const STATIC_A: &'static Handler = &Handler { handle: aaa }; 

struct Foo { 
    data: &'static str, 
    handler: &'static Handler, 
} 

fn main() { 
    let foo = Foo { data: "aaa", handler: STATIC_A }; 
    println!("{}", (foo.handler.handle)(3)); 
} 

它的人體工程學設計較差,但它也小了64位!

+0

不,正如我指出的那樣,我已經使用了這樣的代碼。問題是我有許多像'aaa'這樣的函數並對它們進行組織我需要類似特性的東西來簡化在代碼中找到合適的功能並向其他人展示應實現的接口 – user1244932

+0

已更新;在這種情況下,我建議使用trait對象,除非你真的有嚴格的要求,但是以防萬一我展示瞭如何自己創建一個虛擬表。 –

相關問題