Iteración sobre un rango con paso no estándar en Rust: 3 formas

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Cómo recorrer números con paso 3, 5 o 100 en Rust? Análisis completo



Imagina: estás programando en Rust y necesitas mostrar todos los números pares del 0 al 20. O, digamos, construir la secuencia 0, 5, 10, 15, 20. En Python simplemente escribirías range(0, 21, 5) — y listo. ¿Y en Rust? Aquí hay matices, y hoy los analizaremos.



Esta pregunta es una de las más populares en Stack Overflow (miles de visitas). Y no es de extrañar: el estándar for i in 0..10 solo funciona con paso 1. ¿Y si necesitas otro paso? Resulta que hay tres formas, cada una con sus ventajas y desventajas.



No solo mostraremos el código — compararemos los enfoques en cuanto a rendimiento, legibilidad y flexibilidad. ¡Allá vamos!



¿Por qué Rust no tiene un rango incorporado con paso?



La respuesta corta: porque Range en Rust es un iterador simple que genera una secuencia de números enteros. No admite un paso por defecto para mantenerse lo más ligero y predecible posible. Pero los desarrolladores proporcionaron métodos que resuelven esta tarea.



La respuesta larga: en el ecosistema de Rust, la filosofía es "explícito es mejor que implícito". En lugar de meter el paso en la sintaxis del rango, los creadores ofrecieron herramientas flexibles de adaptación de iteradores. Y esto abre más posibilidades de las que parece.



Forma 1: step_by() — el camino más directo



En la biblioteca estándar de Rust existe el método step_by(n), que crea un iterador que salta cada n-1 elementos. Así funciona:



fn main() {    // Mostramos números del 0 al 20 con paso 5    for i in (0..=20).step_by(5) {        println!("{}", i);    }    // Salida: 0, 5, 10, 15, 20}


Observa: usé 0..=20 (inclusivo), no 0..20 (exclusivo). Si necesitas paso 3 hasta 10, escribe (0..10).step_by(3) — obtendrás 0, 3, 6, 9.



Ventajas de step_by():

  • Simplicidad — una línea, sin closures
  • Legibilidad — se ve claramente lo que hace
  • Rendimiento — gastos generales mínimos


Desventajas:

  • Solo funciona con números enteros (i32, u64, etc.)
  • No se puede establecer un paso dinámico en cada iteración
  • Apareció solo en Rust 1.28 (pero ya hace tiempo)


Este método es la elección ideal para el 90% de las tareas. Si solo necesitas recorrer números con un paso constante — usa step_by().



Forma 2: filter() — cuando el paso es una condición



¿Y si el paso no es constante? Por ejemplo, necesitas mostrar números que sean divisibles por 3 sin resto. O números donde la suma de dígitos sea mayor que 5. Aquí viene al rescate filter():



fn main() {    // Todos los números del 0 al 30, múltiplos de 4    for i in (0..=30).filter(|x| x % 4 == 0) {        println!("{}", i);    }    // Salida: 0, 4, 8, 12, 16, 20, 24, 28

// Y ahora — números que no son divisibles por 3 for i in (0..20).filter(|x| x % 3 != 0) { println!("{}", i); } // Salida: 1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19}


Aquí pasamos un closure que verifica cada número. filter() solo deja aquellos elementos para los cuales la condición es verdadera.



¿Cuándo filter() es mejor que step_by():

  • El paso depende del valor (ej.: "todos los números primos")
  • Necesitas combinarlo con otras condiciones
  • El paso no es constante (ej.: números de Fibonacci)


Desventajas:

  • Un poco más lento que step_by() — hay que verificar cada número
  • El código puede ser menos obvio para principiantes


Personalmente, uso filter() cuando no solo necesito "cada n-ésimo", sino "cada uno que cumpla una regla". Es más potente, pero requiere más atención.

Blogs

Book Recommendations