问题描述
我目前正在尝试用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>>;
调用方无法选择类型;编译器从函数体推断实际类型。