Me ha resultado útil dejar que el compilador me guíe:
fn to_words(text: &str) { // Note no return type
text.split(' ')
}
La compilación da:
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> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Siguiendo la sugerencia del compilador y copiando y pegando eso como mi tipo de retorno (con un poco de limpieza):
use std::str;
fn to_words(text: &str) -> str::Split<'_, char> {
text.split(' ')
}
El problema es que no puedes devolver un rasgo como Iterator
porque un rasgo no tiene un tamaño. Eso significa que Rust no sabe cuánto espacio asignar para el tipo. Usted no puede devolver una referencia a una variable local, ya sea , por lo que volver &dyn Iterator
es un non-starter.
Rasgo implícito
A partir de Rust 1.26, puede utilizar 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());
}
Existen restricciones sobre cómo se puede utilizar. Solo puede devolver un solo tipo (¡sin condicionales!) Y debe usarse en una función libre o una implementación inherente.
En caja
Si no le importa perder un poco de eficiencia, puede devolver 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());
}
Esta es la opción principal que permite el envío dinámico . Es decir, la implementación exacta del código se decide en tiempo de ejecución, en lugar de en tiempo de compilación. Eso significa que es adecuado para los casos en los que necesita devolver más de un tipo concreto de iterador en función de una condición.
Nuevo tipo
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());
}
Escriba alias
Como lo señaló 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());
}
Lidiando con cierres
Cuando impl Trait
no está disponible para su uso, los cierres complican las cosas. Los cierres crean tipos anónimos y estos no se pueden nombrar en el tipo de retorno:
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]>`
En ciertos casos, estos cierres se pueden reemplazar con funciones, que se pueden nombrar:
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>`
Y siguiendo los consejos anteriores:
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)
}
Tratar con condicionales
Si necesita elegir condicionalmente un iterador, consulte Reiterar condicionalmente uno de varios posibles iteradores .