2017-07-18 99 views
0

我創建了一個HashMap,它將字符串映射到類型爲Vec<Expression> -> Expression的函數,其中Expression是我定義的類型。有問題的代碼是:函數的Rust HashMap的類型簽名

let functions: HashMap<_, _> = vec!(("+", Box::new(plus))).into_iter().collect(); 

如果我讓拉斯特推斷類型對我來說,在上面的代碼,它編譯並運行正常,因爲在上面的代碼。但是,如果我嘗試指定類型,它不會編譯:

let functions: HashMap<&str, Box<Fn(Vec<Expression>) -> Expression>> = 
    vec!(("+", Box::new(plus))).into_iter().collect(); 

編譯器錯誤消息不是非常有幫助:

let functions: HashMap<&str, Box<Fn(Vec<Expression>) -> Expression>> = vec!(("+", Box::new(plus))).into_iter().collect(); 
^^^^^^^ a collection of type `std::collections::HashMap<&str, std::boxed::Box<std::ops::Fn(std::vec::Vec<Expression>) -> Expression>>` cannot be built from an iterator over elements of type `(&str, std::boxed::Box<fn(std::vec::Vec<Expression>) -> Expression {plus}>)` 

什麼實際類型這個HashMap的?

+0

看來我的谷歌福是真的很弱,我敢發誓,有這種重複:( –

回答

1

如果你仔細觀察差異,你會得到你的答案,儘管它可能令人費解。

我希望plus已被宣佈爲:

fn plus(v: Vec<Expression>) -> Expression; 

在這種情況下,plus類型是fn(Vec<Expression>) -> Expression {plus},居然一個Voldemort Type是:它不能被命名。

最值得注意的是,它與最終的fn(Vec<Expression>) -> Expression {multiply}不同。

這兩種類型可以被強制轉換爲裸號fn(Vec<Expression>) -> Expression(不包括{plus}/{multiply}面額)。

而後一種類型可以轉換爲Fn(Vec<Expression>) -> Expression,這是任何可調用的特性,不會修改它們的環境(例如關閉|v: Vec<Expression>| v[0].clone())。


的問題,但是,是同時fn(a) -> b {plus}可以轉化爲其中fn(a) -> b可以轉化爲Fn(a) -> b ...的轉換需要存儲器表示的變化。這是因爲:

  • fn(a) -> b {plus}是一個零大小的類型,
  • fn(a) -> b是指向功能,
  • Box<Fn(a) -> b>被裝箱性狀對象這通常意味着兩個虛擬指針一個數據指針。

因此,類型歸屬不起作用,因爲它只能執行無成本的強制。


的解決方案是執行轉換爲時已晚之前:

// Not strictly necessary, but it does make code shorter. 
type FnExpr = Box<Fn(Vec<Expression>) -> Expression>; 

let functions: HashMap<_, _> = 
    vec!(("+", Box::new(plus) as FnExpr)).into_iter().collect(); 
       ^~~~~~~~~~~~~~~~~~~~~~~~ 

或者,也許你寧願保持拆箱功能:

// Simple functions only 
type FnExpr = fn(Vec<Expression>) -> Expression; 

let functions: HashMap<_, _> = 
    vec!(("+", plus as FnExpr)).into_iter().collect(); 
+0

順便說一句,'FN(A) - > B {加}'是不是指針[它是零大小。](https://play.rust-lang.org/?gist=0ad4ee6d789294e3af47d0a71dc74940&version=stable) – red75prime

+0

@ red75prime:哦,對,我總是忘記這件整潔的東西! –

+0

感謝您的詳細和全面的答案這完全回答了我的問題。 – isaacg

2

錯誤消息的相關部分Box<std::ops::Fn ... >Box<fn ... {plus}>。首先是盒裝Fn特質對象。第二個是盒裝功能plus。請注意,它不是指向函數的方塊指針,它將是Box<fn ...>而沒有{plus}部分。它是功能plus本身的獨特和不可名字的類型。

那就是你不能寫這個HashMap的實際類型,因爲它包含的類型是不可命名的。這不是什麼大不了的,你只能把plus函數加入它。

下面的代碼提供編譯錯誤

let functions: HashMap<_, _> = 
    vec![("+", Box::new(plus)), 
     ("-", Box::new(minus))].into_iter().collect(); 
         ^^^^^ expected fn item, found a different fn item 

這工作,但它是無用

let functions: HashMap<_, _> = 
    vec![("+", Box::new(plus)), 
     ("-", Box::new(plus))].into_iter().collect(); 

一種可能的解決方案是將一個向量的第一元素轉換成所需要的類型。

type BoxedFn = Box<Fn(Vec<Expression>) -> Expression>; 

let functions: HashMap<&str, BoxedFn> = 
    vec![("+", Box::new(plus) as BoxedFn), 
     ("_", Box::new(minus))].into_iter().collect(); 

另一個是中間變量的類型歸屬。

type BoxedFn = Box<Fn(Vec<Expression>) -> Expression>; 

let v: Vec<(_, BoxedFn)> = vec![("+", Box::new(plus)), ("_", Box::new(minus))]; 
let functions: HashMap<&str, BoxedFn> = v.into_iter().collect();