rust - que - ¿Cuál es la forma correcta de devolver un iterador(o cualquier otro rasgo)?
recorrer arraylist java foreach (1)
Me ha resultado útil dejar que el compilador me guíe:
fn to_words(text: &str) { // Note no return type
text.split('' '')
}
Compilar 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 pegando copias 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 puede 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.
Tampoco puede devolver una referencia a una variable local, por
lo que devolver
&dyn Iterator
no se inicia.
Rasgo implícito
A partir de Rust 1.26, puede usar el
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 usar esto. 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 despacho 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 esto es adecuado para 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());
}
Tratar con cierres
Cuando el
impl Trait
no está disponible para su uso, los cierres complican las cosas.
Los cierres crean tipos anónimos y 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)
}
Manejo de condicionales
Si necesita elegir condicionalmente un iterador, consulte Condicionalmente iterar sobre uno de varios iteradores posibles .
El siguiente código de Rust se compila y se ejecuta sin problemas.
fn main() {
let text = "abc";
println!("{}", text.split('' '').take(2).count());
}
Después de eso, intenté algo como esto ... pero no se compiló
fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}
fn to_words(text: &str) -> &Iterator<Item = &str> {
&(text.split('' ''))
}
El principal problema es que no estoy seguro de qué tipo de retorno debería tener la función
to_words()
.
El compilador dice:
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`
¿Cuál sería el código correcto para hacer que esto se ejecute? .... y dónde está mi brecha de conocimiento?