以下 Rust 代码编译并运行没有任何问题。
fn main() { let text = "abc"; println!("{}", text.split(' ').take(2).count()); }
在那之后,我尝试了这样的事情....但它没有编译
fn main() { let text = "word1 word2 word3"; println!("{}", to_words(text).take(2).count()); } fn to_words(text: &str) -> &Iterator<Item = &str> { &(text.split(' ')) }
主要问题是我不确定该函数to_words()应该具有什么返回类型。编译器说:
to_words()
error[E0599]: no method named `count` found for type `std::iter::Take<std::iter::Iterator<Item=&str>>` in the current scope --> src/main.rs:3:43 | 3 | println!("{}", to_words(text).take(2).count()); | ^^^^^ | = note: the method `count` exists but the following trait bounds were not satisfied: `std::iter::Iterator<Item=&str> : std::marker::Sized` `std::iter::Take<std::iter::Iterator<Item=&str>> : std::iter::Iterator`
运行此程序的正确代码是什么?....我的知识差距在哪里?
我发现让编译器指导我很有用:
fn to_words(text: &str) { // Note no return type text.split(' ') }
编译给出:
error[E0308]: mismatched types --> src/lib.rs:5:5 | 5 | text.split(' ') | ^^^^^^^^^^^^^^^ expected (), found struct `std::str::Split` | = note: expected type `()` found type `std::str::Split<'_, char>` help: try adding a semicolon | 5 | text.split(' '); | ^ help: try adding a return type | 3 | fn to_words(text: &str) -> std::str::Split<'_, char> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
按照编译器的建议并将其复制粘贴为我的返回类型(稍作清理):
use std::str; fn to_words(text: &str) -> str::Split<'_, char> { text.split(' ') }
问题是你不能返回一个特征,Iterator因为一个特征没有大小。这意味着 Rust 不知道要为该类型分配多少空间。您也不能返回对局部变量的引用,因此返回&dyn Iterator是不可行的。
Iterator
&dyn Iterator
从 Rust 1.26 开始,您可以使用impl trait:
impl trait
fn to_words<'a>(text: &'a str) -> impl Iterator<Item = &'a str> { text.split(' ') } fn main() { let text = "word1 word2 word3"; println!("{}", to_words(text).take(2).count()); }
如何使用它是有限制的。您只能返回一个类型(没有条件!),它必须用于自由函数或固有实现。
如果您不介意损失一点效率,可以返回 a Box<dyn Iterator>:
Box<dyn Iterator>
fn to_words<'a>(text: &'a str) -> Box<dyn Iterator<Item = &'a str> + 'a> { Box::new(text.split(' ')) } fn main() { let text = "word1 word2 word3"; println!("{}", to_words(text).take(2).count()); }
这是允许 动态调度 的主要选项。也就是说,代码的确切实现是在运行时决定的,而不是在编译时决定的。这意味着这适用于需要根据条件返回多个具体类型的迭代器的情况。
use std::str; struct Wrapper<'a>(str::Split<'a, char>); impl<'a> Iterator for Wrapper<'a> { type Item = &'a str; fn next(&mut self) -> Option<&'a str> { self.0.next() } fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() } } fn to_words(text: &str) -> Wrapper<'_> { Wrapper(text.split(' ')) } fn main() { let text = "word1 word2 word3"; println!("{}", to_words(text).take(2).count()); }
正如reem所指出的
use std::str; type MyIter<'a> = str::Split<'a, char>; fn to_words(text: &str) -> MyIter<'_> { text.split(' ') } fn main() { let text = "word1 word2 word3"; println!("{}", to_words(text).take(2).count()); }
当impl Trait不可用时,闭包会使事情变得更加复杂。闭包创建匿名类型,这些不能在返回类型中命名:
impl Trait
fn odd_numbers() -> () { (0..100).filter(|&v| v % 2 != 0) } found type `std::iter::Filter<std::ops::Range<{integer}>, [closure@src/lib.rs:4:21: 4:36]>`
在某些情况下,这些闭包可以用函数替换,可以命名为:
fn odd_numbers() -> () { fn f(&v: &i32) -> bool { v % 2 != 0 } (0..100).filter(f as fn(v: &i32) -> bool) } found type `std::iter::Filter<std::ops::Range<i32>, for<'r> fn(&'r i32) -> bool>`
并遵循上述建议:
use std::{iter::Filter, ops::Range}; type Odds = Filter<Range<i32>, fn(&i32) -> bool>; fn odd_numbers() -> Odds { fn f(&v: &i32) -> bool { v % 2 != 0 } (0..100).filter(f as fn(v: &i32) -> bool) }