Zauważyłem, że warto pozwolić kompilatorowi mnie prowadzić:
fn to_words(text: &str) { // Note no return type
text.split(' ')
}
Kompilacja daje:
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> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Postępując zgodnie z sugestią kompilatora i kopiując i wklejając to jako mój typ zwrotu (z niewielkim porządkiem):
use std::str;
fn to_words(text: &str) -> str::Split<'_, char> {
text.split(' ')
}
Problem polega na tym, że nie możesz zwrócić cechy takiej jak, Iterator
ponieważ cecha nie ma rozmiaru. Oznacza to, że Rust nie wie, ile miejsca przeznaczyć na typ. Nie możesz również zwrócić odwołania do zmiennej lokalnej , więc zwracanie nie &dyn Iterator
jest początkowe.
Impl cecha
Od wersji Rust 1.26 możesz używać 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());
}
Istnieją ograniczenia dotyczące tego, jak można tego używać. Możesz zwrócić tylko jeden typ (bez warunków!) I musi być użyty w wolnej funkcji lub wewnętrznej implementacji.
W pudełku
Jeśli nie masz nic przeciwko utracie odrobiny wydajności, możesz zwrócić 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());
}
Jest to podstawowa opcja umożliwiająca dynamiczną wysyłkę . Oznacza to, że o dokładnej implementacji kodu decyduje się w czasie wykonywania, a nie kompilacji. Oznacza to, że jest to odpowiednie w przypadkach, w których musisz zwrócić więcej niż jeden konkretny typ iteratora na podstawie warunku.
Nowy typ
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());
}
Wpisz alias
Jak wskazano w 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());
}
Radzenie sobie z zamknięciami
Kiedy impl Trait
nie jest dostępny do użytku, zamknięcia komplikują sprawę. Zamknięcia tworzą anonimowe typy, których nie można nazwać w zwracanym typie:
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]>`
W niektórych przypadkach te zamknięcia można zastąpić funkcjami, które można nazwać:
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>`
I postępując zgodnie z powyższą radą:
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)
}
Radzenie sobie z warunkami
Jeśli chcesz warunkowo wybrać iterator, zapoznaj się z tematem Warunkowe iteracje po jednym z kilku możliwych iteratorów .