2017-06-17 49 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类型”这个术语,这种类型不应该被命名(不能,真的)。 –