¿Puedo especificar el tipo de iteradores basado en clausuras?
Escribí una función para iterar sobre los vecinos de las celdas en una cuadrícula 2D:
pub fn neighbours(
(width, height): (usize, usize),
(x, y): (usize, usize),
) -> impl Iterator<item =="" (usize,="" usize)=""> {
[(-1, 0), (1, 0), (0, -1), (0, 1)]
.iter()
.map(move |(dx, dy)| (x as i64 + dx, y as i64 + dy))
.filter(move |&(nx, ny)| 0 <= ny && ny < height as i64 && 0 <= nx && nx < width as i64)
.map(|(nx, ny)| (nx as usize, ny as usize))
}
Ten en cuenta que devuelve una impl Iterator<item =="" (usize,="" usize)="">
.
Si entiendo correctamente, devolver una impl
resultaría en código más lento, llamando a punteros de función en lugar de compilar las cosas en bucles simples. ¿Verdad?
Por lo tanto, queriendo especificar un tipo más exacto, reemplacé el tipo con ()
para ver qué tipo infiere el compilador, y el resultado fue:
std::iter::Map<><><std::slice::iter<'l, (i64,="" i64)="">, _, _>, _>, _>
Donde los _
representan closures cuyos tipos no sé cómo especificar.
Intenté extraer los closures a structs con traits Fn
pero no pude hacer que funcione y además, según entiendo, implementar traits Fn
es una característica inestable y no debería usarla”.</std::slice::iter<‘l,>
davy.ai
Sí, devolver una
impl Iterator
puede resultar en un código más lento y con cierta sobrecarga, ya que el compilador necesita generar un nuevo tipo para cada implementación del traitIterator
devuelto por la función. Sin embargo, en algunos casos también puede permitir un código más flexible y genérico.En tu caso, es correcto que al reemplazar el tipo de retorno por
()
le permite al compilador inferir un tipo más específico para el iterador, lo que potencialmente puede resultar en un código más rápido. Sin embargo, el tipo resultante puede ser bastante complejo y difícil de leer, como has señalado.Extraer los closures a structs con traits
Fn
es una posible solución, pero como también has mencionado, esta es una característica inestable de Rust y no se recomienda para uso general.Otro enfoque que podrías probar es usar la sintaxis
impl Trait
para especificar un tipo de retorno concreto, al tiempo que permite cierta flexibilidad en la implementación. Por ejemplo, podrías definir un alias de tipo para el tipo del iterador:Y luego usarlo como tipo de retorno de tu función
neighbours
:Esto le dice al compilador que el tipo de retorno de la función
neighbours
debe ser un tipo que implemente el traitIterator
con un tipoItem
de(usize, usize)
, pero permite que el tipo real sea determinado por la implementación de la función.Este enfoque puede proporcionar un buen equilibrio entre flexibilidad y rendimiento, y también debería hacer que el tipo resultante sea más fácil de leer y comprender.