Ответ 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)
}
Работа с условиями
Если вам необходимо условно выбрать итератор, обратитесь к разделу "Условно итерация" для одного из нескольких возможных итераторов.