关闭Rust函数的返回类型

时间:2017-06-17 21:13:24

标签: 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 `[closure@src/main.rs:22:21: 22:50]`

error: aborting due to previous error(s)

error: Could not compile `iterator_return_type`.

我发现特别令人困惑的是最终的编译错误。我不明白我怎么可能指定一个闭包类型。

谢谢!

1 个答案:

答案 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())
}

如果您想使用稳定的Rust怎么办?不幸的是,你现在必须使用拳击。

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