J'ai trouvé utile de laisser le compilateur me guider:
fn to_words(text: &str) { // Note no return type
text.split(' ')
}
La compilation donne:
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> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
En suivant la suggestion du compilateur et en copiant-collant cela comme type de retour (avec un peu de nettoyage):
use std::str;
fn to_words(text: &str) -> str::Split<'_, char> {
text.split(' ')
}
Le problème est que vous ne pouvez pas renvoyer un trait comme Iterator
parce qu'un trait n'a pas de taille. Cela signifie que Rust ne sait pas combien d'espace à allouer pour le type. Vous ne pouvez pas non plus renvoyer une référence à une variable locale , donc le retour &dyn Iterator
est un non-starter.
Trait d'impl
À partir de Rust 1.26, vous pouvez utiliser 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());
}
Il existe des restrictions sur la manière dont cela peut être utilisé. Vous ne pouvez renvoyer qu'un seul type (pas de condition!) Et il doit être utilisé sur une fonction libre ou une implémentation inhérente.
En boîte
Si cela ne vous dérange pas de perdre un peu d'efficacité, vous pouvez renvoyer un 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());
}
Il s'agit de la principale option qui permet une répartition dynamique . Autrement dit, l'implémentation exacte du code est décidée au moment de l'exécution, plutôt qu'au moment de la compilation. Cela signifie que cela convient aux cas où vous devez renvoyer plus d'un type concret d'itérateur en fonction d'une condition.
Nouveau genre
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());
}
Tapez un alias
Comme l'a souligné 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());
}
Faire face aux fermetures
Lorsqu'elles impl Trait
ne sont pas disponibles, les fermetures compliquent les choses. Les fermetures créent des types anonymes et ceux-ci ne peuvent pas être nommés dans le type de retour:
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]>`
Dans certains cas, ces fermetures peuvent être remplacées par des fonctions, qui peuvent être nommées:
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>`
Et en suivant les conseils ci-dessus:
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)
}
Gérer les conditions
Si vous avez besoin de choisir conditionnellement un itérateur, reportez-vous à Itération conditionnelle sur l'un des itérateurs possibles .
type
alias à la place, car l'utilisation d'un nouveau type signifie que votre Iterator n'implémentera pas de traits commeRandomAccessIterator
même si l'itérateur sous-jacent le fait.type LibraryResult<T> = Result<T, LibraryError>
comme une commodité similaire àIoResult<T>
, qui est également juste un alias de type.'a
vie àBox
? Qu'est-ce que ça veut dire? J'ai toujours pensé que c'était pour les limites seulement, pour dire "T ne peut dépendre que de quelque chose vivant au moins aussi longtemps que'a
".