2017-06-17 70 views
1

我編寫了下面的Rust程序來打印出只有整數的命令行參數。它完美的作品:Rust函數的返回類型中的關閉

use std::env; 
fn main() { 
    for i in env::args().filter_map(|arg| arg.parse::<i32>().ok()) { 
     println!("{}", i); 
    } 
} 

然後,我試圖重新編寫程序將過濾器抽象爲函數。這個版本不能編譯。

use std::env::Args; 
use std::env; 
use std::iter::FilterMap; 
// Version 2 
fn main() { 
    for i in nums(&env::args()) { 
     println!("{}", i); 
    } 
} 

fn nums<F: Fn(String) -> Option<i32>>(args: &Args) -> FilterMap<Args,F> { 
    args.filter_map(|arg| arg.parse::<i32>().ok()) 
} 

它產生以下編譯錯誤:

Compiling iterator_return_type v0.1.0 (file:///Users/gabriel/AllProjects/SentimentAnalysis/iterator_return_type) 
error[E0282]: type annotations needed 
    --> src/main.rs:16:9 
    | 
16 |  for i in nums(&env::args()) { 
    |  ^cannot infer type for `_` 

error: the type of this value must be known in this context 
    --> src/main.rs:22:27 
    | 
22 |  args.filter_map(|arg| arg.parse::<i32>().ok()) 
    |       ^^^^^^^^^^^^^^^^^^ 

error[E0308]: mismatched types 
    --> src/main.rs:22:21 
    | 
22 |  args.filter_map(|arg| arg.parse::<i32>().ok()) 
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found closure 
    | 
    = note: expected type `F` 
       found type `[[email protected]/main.rs:22:21: 22:50]` 

error: aborting due to previous error(s) 

error: Could not compile `iterator_return_type`. 

我覺得特別困惑什麼是最終的編譯錯誤。我不明白我還可以指定一個閉包類型。

謝謝!

+0

該問題給出的解決方案不適用於我的示例。在那個問題中,Split迭代器是返回類型。 FilterMap迭代器需要一個閉包類型參數。這是一個完全不同的問題。 –

+0

您應該認真考慮重命名問題的標題,因爲它強烈建議重複上述建議。你似乎有更多的「不能引用封閉類型」的問題。 –

+0

好的建議,謝謝!我已經這樣做了。 –

回答

2

impl TraitBox<Trait>解決方案可以應用於迭代器和閉包,它們都只是特徵!不同的是你必須在關閉情況下使用它們。

如果你想使用impl Trait,那麼你的代碼看起來就像這樣(請注意,Args應該按值傳遞):

#![feature(conservative_impl_trait)] 

use std::env::Args; 
use std::env; 
use std::iter::FilterMap; 

fn main() { 
    for i in nums(env::args()) { 
     println!("{}", i); 
    } 
} 

fn nums(args: Args) -> FilterMap<Args, impl FnMut(String) -> Option<i32>> { 
    args.filter_map(|arg| arg.parse::<i32>().ok()) 
} 

但是,你通常需要不暴露迭代器類型的細節;因此你可以這樣做:

fn nums(args: Args) -> impl Iterator<Item = i32> { 
    args.filter_map(|arg| arg.parse::<i32>().ok()) 
} 

如果你想使用穩定的鏽劑怎麼辦?不幸的是,你現在必須使用拳擊。

fn nums(args: Args) -> Box<Iterator<Item = i32>> { 
    Box::new(args.filter_map(|arg| arg.parse::<i32>().ok())) 
} 

你爲什麼不能描述一個完整的閉合型的,儘管你能描述像Zip<Drain<'a, i32>, IntoIter<&'b str>>一個迭代器?有兩個原因:

  • 封閉類型本質上是匿名的;如果你想退貨,你必須匿名(impl Fn())或方框(Box<Fn()>)。
  • 閉合性狀的界面不穩定;您無法穩定實施它們(impl Fn() for YourType { .. })。

那麼爲什麼你的代碼不工作?其原因是:

  • 如果你想傳遞閉包一個功能,來電顯示決定其類型。在這種情況下,你可以寫fn foo<T: Fn()>() { .. }
  • 如果您想通過函數,被調用者決定它的類型。在這種情況下,您必須使用impl Trait

RFC 1951將改變這種區別。在這兩種情況下,您都可以使用impl Trait

+0

另一個優秀的答案!我希望在Rust標籤上歡迎您,我期待您的未來貢獻!關於「匿名」類型的觀點,編程社區似乎正在聚焦於「Voldemort類型」這個術語,這種類型不應該被命名(不能,真的)。 –