Introducción: ¿Por qué buscar un índice en Rust no es simplemente "encontrar y devolver"?
\n\nCada desarrollador que migra a Rust desde Python o JavaScript, tarde o temprano se pregunta: "¿Cómo encuentro aquí el índice de un elemento en un array o vector?" En Python se hace con una línea list.index(element), en JavaScript con array.indexOf(element). Pero Rust, como un verdadero perfeccionista, ofrece no una, sino varias formas, cada una con sus propios matices de rendimiento y seguridad.
Analicemos cómo encontrar el índice de un elemento en las colecciones de Rust (arrays, vectores y slices) de manera que el código no solo sea correcto, sino también rápido. ¡Porque en la programación de sistemas, cada nanosegundo cuenta!
\n\nConceptos básicos: métodos principales para buscar un índice en Rust
\n\nEn la biblioteca estándar de Rust hay dos "jugadores" principales para buscar un índice: position() y rposition(). Están disponibles para cualquier iterador, y por lo tanto también para arrays, vectores y slices.
Método position(): búsqueda de izquierda a derecha
\n\nposition() toma un closure y devuelve un Option<usize> — el índice del primer elemento que cumple la condición, o None si no se encuentra dicho elemento.
fn main() {\n let numbers = vec![10, 20, 30, 40, 50];\n \n // Buscamos la primera aparición del número 30\n let index = numbers.iter().position(|&x| x == 30);\n \n match index {\n Some(i) => println!("Índice del número 30: {}", i), // Salida: 2\n None => println!("Elemento no encontrado"),\n }\n}\n\n\nTenga en cuenta: llamamos a iter() antes de position(). Esto es importante, ya que position() es un método del trait Iterator, no de la colección en sí.
Método rposition(): búsqueda de derecha a izquierda
\n\nSi necesita el último índice de aparición de un elemento, use rposition(). Funciona de manera similar, pero recorre la colección desde el final:
fn main() {\n let data = vec!['a', 'b', 'c', 'b', 'a'];\n \n // Encontremos la última aparición de 'b'\n let last_b = data.iter().rposition(|&c| c == 'b');\n \n println!("Última 'b' en el índice: {:?}", last_b); // Some(3)\n}\n\n\nUna limitación importante: rposition() solo funciona con colecciones que admiten el recorrido bidireccional (trait DoubleEndedIterator). Afortunadamente, todas las colecciones estándar de Rust cumplen con este requisito.
Comparación de rendimiento: position() vs bucle manual
\n\nMuchos principiantes escriben bucles manualmente, pensando que es más rápido. Verifiquemos esta suposición en la práctica.
\n\nConsideremos dos enfoques para buscar el índice de un elemento en un vector de 1 millón de números enteros:
\n\nuse std::time::Instant;\n\nfn main() {\n let large_vec: Vec<i32> = (0..1_000_000).collect();\n let target = 999_999;\n \n // Método 1: position()\n let start = Instant::now();\n let pos1 = large_vec.iter().position(|&x| x == target);\n let duration1 = start.elapsed();\n println!("position(): {:?}, tiempo: {:?}", pos1, duration1);\n \n // Método 2: bucle manual\n let start = Instant::now();\n let mut pos2 = None;\n for (i, &val) in large_vec.iter().enumerate() {\n if val == target {\n pos2 = Some(i);\n break;\n }\n }\n let duration2 = start.elapsed();\n println!("Bucle manual: {:?}, tiempo: {:?}", pos2, duration2);\n}\n\n\nLos resultados en procesadores modernos generalmente muestran que position() funciona tan rápido como el bucle manual, y a veces incluso más rápido gracias a las optimizaciones internas del compilador de Rust (LLVM). Conclusión: use position() — es idiomático, seguro y no es inferior en velocidad.
Búsqueda binaria: cuando los datos están ordenados
\n\nSi su colección está ordenada, debe usar binary_se