En Rust, los rangos son una forma elegante de expresar una secuencia de valores. Estos no son simples valores, sino tipos de datos que pertenecen al módulo std::ops. Su propósito principal es facilitar la iteración de números, la creación de porciones de colecciones (slicing) y la comprobación de si un elemento pertenece a un intervalo determinado.
Existen diferentes tipos de rangos según cómo definas sus límites:
RangeFull(..): Representa todos los valores posibles de un tipo.Range(a..b): Es un rango semiabierto. Empieza enay termina justo antes deb. Este es el estándar para trabajar con índices de arreglos porque evita el error de “fuera de rango” al usar el tamaño de la colección como límite superior.RangeInclusive(a..=b): Es un rango cerrado. Incluye tanto el valor inicial como el valor final.- Rangos incompletos: Puedes usar
a..para indicar “desdeahasta el infinito” (RangeFrom), o..bpara indicar “desde el inicio hastab” (RangeTo).
Un error muy común en principiantes es el error de “off-by-one” (desfase por uno). Si intentas acceder a un elemento usando un Range (..) creyendo que el límite final está incluido, obtendrás un error de lógica o un error de compilación. Por ejemplo, en un arreglo de 5 elementos, los índices son del 0 al 4; usar 0..5 es correcto para iterar, pero 0..4 solo llegará hasta el índice 3.
Los rangos implementan el trait IntoIterator, lo que significa que pueden ser usados directamente en bucles for sin necesidad de llamar a métodos adicionales para convertirlos en iteradores.
fn main() {
// Un arreglo de ejemplo para demostrar slicing y rangos
let numeros = [10, 20, 30, 40, 50, 60];
// 1. Uso de Range (excluyente) en un bucle for
// Imprime los números del 0 al 4
println!("Iteración con Range (0..5):");
for i in 0..5 {
print!("{} ", i);
}
println!("\n");
// 2. Uso de RangeInclusive (incluyente) en un bucle for
// Imprime los números del 0 al 5
println!("Iteración con RangeInclusive (0..=5):");
for i in 0..=5 {
print!("{} ", i);
}
println!("\n");
// 3. Slicing: obtener una porción de un arreglo mediante rangos
// El segundo valor es el límite superior (no incluido)
let sub_slice = &numeros[1..4];
println!("Sub-slice de 'numeros' (índices 1 al 3): {:?}", sub_slice);
// 4. Verificación de pertenencia con .contains()
let valor_a_buscar = 40;
let rango_excluyente = 10..40; // Tipo Range
let rango_incluyente = 10..=40; // Tipo RangeInclusive
println!("¿El valor {} está en 10..40?: {}", valor_a_buscar, rango_excluyente.contains(&valor_a_buscar));
println!("¿El valor {} está en 10..=40?: {}", valor_a_buscar, rango_incluyente.contains(&valor_a_buscar));
}
Explicación del Código
* numeros: Es un arreglo de tipo [i32; 6].
* i: Es la variable de control del bucle for que toma los valores generados por el rango.
* 0..5: Un Range que genera los valores 0, 1, 2, 3, 4. El 5 es el límite pero no se incluye.
* 0..=5: Un RangeInclusive que genera los valores 0, 1, 2, 3, 4, 5.
* sub_slice: Es una referencia (slice) que apunta a una parte del arreglo numeros. Al usar 1..4, tomamos los elementos en las posiciones de índice 1, 2 y 3.
* rango_excluyente: Es un objeto de tipo Range. Al usar contains(&40), devuelve false porque el límite superior no se incluye.
* rango_incluyente: Es un objeto de tipo RangeInclusive. Al usar contains(&40), devuelve true porque el límite superior sí se incluye.
* valor_a_buscar: Es una variable de tipo i32 que pasamos por referencia (&) al método .contains().
N° 20