返回迭代器(或任何其他特征)的正确方法是什么? [英] What is the correct way to return an Iterator (or any other trait)?

查看:61
本文介绍了返回迭代器(或任何其他特征)的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下 Rust 代码编译和运行没有任何问题.

The following Rust code compiles and runs without any issues.

fn main() {
    let text = "abc";
    println!("{}", text.split(' ').take(2).count());
}

在那之后,我尝试了这样的事情......但它没有编译

After that, I tried something like this .... but it didn't compile

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() 应该具有什么返回类型.编译器说:

The main problem is that I'm not sure what return type the function to_words() should have. The compiler says:

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`

执行此操作的正确代码是什么?....我的知识差距在哪里?

What would be the correct code to make this run? .... and where is my knowledge gap?

推荐答案

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

I've found it useful to let the compiler guide me:

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> {
  |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

遵循编译器的建议并将其复制粘贴为我的返回类型(稍作清理):

Following the compiler's suggestion and copy-pasting that as my return type (with a little cleanup):

use std::str;

fn to_words(text: &str) -> str::Split<'_, char> {
    text.split(' ')
}

问题是你不能返回像 Iterator 这样的特征,因为特征没有大小.这意味着 Rust 不知道要为该类型分配多少空间.您不能返回对局部变量的引用,或者,因此返回 &dyn Iterator 是一个非启动器.

The problem is that you cannot return a trait like Iterator because a trait doesn't have a size. That means that Rust doesn't know how much space to allocate for the type. You cannot return a reference to a local variable, either, so returning &dyn Iterator is a non-starter.

从 Rust 1.26 开始,您可以使用 impl trait:

As of Rust 1.26, you can use 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());
}

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

There are restrictions on how this can be used. You can only return a single type (no conditionals!) and it must be used on a free function or an inherent implementation.

如果你不介意损失一点效率,你可以返回一个Box:

If you don't mind losing a little bit of efficiency, you can return a 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());
}

这是允许动态调度的主要选项.也就是说,代码的确切实现是在运行时决定的,而不是编译时.这意味着这适用于您需要根据条件返回多个具体类型的迭代器的情况.

This is the primary option that allows for dynamic dispatch. That is, the exact implementation of the code is decided at run-time, rather than compile-time. That means this is suitable for cases where you need to return more than one concrete type of iterator based on a condition.

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 不可用时,闭包会使事情变得更加复杂.闭包创建匿名类型,并且不能在返回类型中命名:

Dealing with closures

When impl Trait isn't available for use, closures make things more complicated. Closures create anonymous types and these cannot be named in the return type:

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]>`

在某些情况下,这些闭包可以替换为函数,可以命名为:

In certain cases, these closures can be replaced with functions, which can be named:

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

处理条件

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

这篇关于返回迭代器(或任何其他特征)的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆