返回Iterator(或任何其他特征)的正确方法是什么?


114

以下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()应该具有哪种返回类型。编译器说:

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`

进行此运行的正确代码是什么?....我的知识鸿沟在哪里?

Answers:


143

我发现让编译器指导我很有用:

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不是启动器。

Impl性状

从Rust 1.26开始,您可以使用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());
}

有关如何使用它的限制。您只能返回一个类型(无条件!),并且必须在自由函数或固有实现上使用它。

盒装

如果您不介意降低效率,则可以返回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());
}

输入别名

雷姆指出

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无法使用时,闭包会使事情变得更加复杂。闭包创建匿名类型,而这些不能在返回类型中命名:

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)
}

处理条件

如果需要有条件地选择一个迭代器,请参阅有条件地迭代多个可能的迭代器之一


谢谢,这对我有很大帮助。让编译器指导您的“技巧”非常有用,将来我一定会使用它。...是的,这很丑陋!我希望RFC成为候选版本。
forgemo 2014年

8
尽管包装器类型可以很好地隐藏复杂性,但我发现最好改用type别名,因为使用新类型意味着您的Iterator不会像RandomAccessIterator底层Iterator那样实现特征。
reem 2014年

4
对!类型别名支持通用参数。例如,许多库都type LibraryResult<T> = Result<T, LibraryError>提供了类似于的便利IoResult<T>,它也只是类型别名。
reem 2014年

1
您能否阐明为什么必须增加一个'a生命期Box?那是什么意思?我一直以为这仅是界限,说“ T可能仅取决于至少活到至少一个'a”的事物。
torkleyy

1
@torkleyy也许stackoverflow.com/q/27790168/155423stackoverflow.com/q/27675554/155423会回答您的问题?如果没有,我鼓励您搜索问题,如果找不到,请提出一个新问题。
Shepmaster
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.