簡體   English   中英

返回迭代器(或任何其他特征)的正確方法是什么?

[英]What is the correct way to return an Iterator (or any other trait)?

以下 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`

使此運行的正確代碼是什么? ....我的知識差距在哪里?

我發現讓編譯器指導我很有用:

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是一個非啟動器。

隱含特征

從 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());
}

類型別名

正如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不可用時,閉包會使事情變得更加復雜。 閉包創建匿名類型,並且不能在返回類型中命名:

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

處理條件

如果您需要有條件地選擇迭代器,請參閱有條件地迭代多個可能的迭代器之一

添加到@Shepmaster 答案。 如果你需要閉包和閉包捕獲一些參數,你可以使用這個例子

fn find_endpoint<'r>(
    descriptors: &'r mut rusb::EndpointDescriptors<'r>,
    direction: rusb::Direction,
) -> FilterMap<
    &mut rusb::EndpointDescriptors<'_>,
    impl FnMut(rusb::EndpointDescriptor<'r>) -> Option<u8>,
> {
    descriptors.filter_map(move |ep| {
        if ep.direction() == direction {
            Some(ep.address())
        } else {
            None
        }
    })
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM