从在RUST中没有泛型参数的函数返回泛型类型

人气:55 发布:2023-01-03 标签: types type-inference rust

问题描述

我目前正在尝试用Rust编写一个小函数,它返回一个迭代器,遍历一种简单的LISP式计算器语言的标记。我遇到了一个编译错误,而我并没有预料到。

我第一次尝试编写该函数是:

fn tokenizer_for<'a, I>(s: &'a str) -> Peekable<I> where I: Iterator<Item=&'a str> {
    s.split_whitespace()
        .flat_map(
            |word| {
                word.replace("(", "( ").replace(")", " )").split_whitespace()
            }
        )
        .peekable()
}

但Rustc回复:

error[E0308]: mismatched types
  --> src/lib.rs:4:5
   |
3  |   fn tokenizer_for<'a, I>(s: &'a str) -> Peekable<I> where I: Iterator<Item=&'a str> {
   |                        -                 ----------- expected `std::iter::Peekable<I>` because of return type
   |                        |
   |                        this type parameter
4  | /     s.split_whitespace()
5  | |         .flat_map(
6  | |             |word| {
7  | |                 word.replace("(", "( ").replace(")", " )").split_whitespace()
8  | |             }
9  | |         )
10 | |         .peekable()
   | |___________________^ expected type parameter `I`, found struct `std::iter::FlatMap`
   |
   = note: expected struct `std::iter::Peekable<I>`
              found struct `std::iter::Peekable<std::iter::FlatMap<std::str::SplitWhitespace<'_>, std::str::SplitWhitespace<'_>, [closure@src/lib.rs:6:13: 8:14]>>`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

我发现在返回类型中使用impl有效(我更喜欢):

fn tokenizer_for(s: &str) -> Peekable<impl Iterator<Item=&str>> {
    s.split_whitespace()
        .flat_map(
            |word| {
                word.replace("(", "( ").replace(")", " )").split_whitespace()
            }
        )
        .peekable()
}

但我希望能够在任一选项之间进行选择,即使后一种尝试似乎奏效了,甚至可能不会产生泛型函数。

在前一种情况下,为什么不能使用where子句指定泛型类型?

我以前使用过where子句来约束出现在返回类型中的泛型参数。仅当泛型参数也出现在函数的参数中时才起作用吗?

任何详细解释此区别的参考资料都会特别有帮助。

推荐答案

在Rust中,当您在Item<...>列表中指定了生存期或类型参数时,将在使用站点选择这些生存期或类型参数。

此函数签名表示函数的调用方可以选择I的类型:

fn tokenizer_for<'a, I>(s: &'a str) -> Peekable<I> where I: Iterator<Item=&'a str>;

但这不会编译,因为返回类型实际上是调用Iter::peekable()的返回类型(即错误消息std::iter::Peekable<std::iter::FlatMap<std::str::SplitWhitespace<'_>, std::str::SplitWhitespace<'_>, [closure@src/lib.rs:6:13: 8:14]>>中的类型)。

另一方面,该签名表示返回类型仅仅是实现Iterator<Item=&str>>

的某个
fn tokenizer_for(s: &str) -> Peekable<impl Iterator<Item=&str>>;

调用方无法选择类型;编译器从函数体推断实际类型。

11