Подтвердить что ты не робот

Каков правильный способ вернуть Итератор (или любой другой признак)?

Следующий код 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`

Каким будет правильный код, чтобы этот запуск выполнялся?.... и где мой пробел в знаниях?

4b9b3361

Ответ 1

Я нашел полезным, чтобы компилятор направлял меня:

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

Введите псевдоним

Как указано в Риме

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}>, [[email protected]/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)
}

Работа с условиями

Если вам необходимо условно выбрать итератор, обратитесь к разделу "Условно итерация" для одного из нескольких возможных итераторов.