Rust en LeetCode: Transición de Python al pensamiento sistémico
\n\nSi has llegado a PythonLib, lo más probable es que ya estés familiarizado con Python. Sabes escribir soluciones concisas en una sola línea y usas el tipado dinámico como algo natural. Pero el mundo no se detiene. Cada vez más, en entrevistas para empresas top (y no solo), te piden escribir código en Rust. ¿Por qué? Porque Rust es un lenguaje que combina la velocidad de C con la seguridad de memoria, y LeetCode es el campo de pruebas ideal para perfeccionar esta habilidad.
\n\nEn este artículo no solo repasaremos la sintaxis. Analizaremos cómo pensar en la paradigma de Rust al resolver problemas algorítmicos, qué estructuras de datos usar y cómo no ahogarte en el borrow checker. ¡Allá vamos!
¿Por qué LeetCode en Rust es un desafío y una ventaja?
\n\nLa mayoría de las soluciones en LeetCode están escritas en Python o Java. Rust te obliga a pensar en memoria, propiedad y tiempos de vida. Parece difícil, pero precisamente eso te hace más fuerte como ingeniero. Al resolver problemas en Rust, automáticamente aprendes:
\n\n- \n
- Sistema de propiedad (Ownership) — la característica principal del lenguaje. \n
- Préstamo (Borrowing) y referencias mutables/inmutables. \n
- Iteradores eficientes sin asignaciones innecesarias. \n
- Trabajo con
OptionyResulten lugar de excepciones. \n
Si vienes de Python, los primeros 10 problemas te parecerán una tortura. Pero cuando entiendas cómo el compilador te protege de null pointer y condiciones de carrera, te enamorarás de este lenguaje.
¿Qué necesitas estudiar antes de empezar?
\n\nAntes de lanzarte a los problemas, revisemos el equipamiento. Sin él, escribirás código como en C++ con macros: ineficiente y doloroso.
\n\n1. Iteradores y clausuras (Closures)
\nEn Rust, los iteradores no son solo un bucle for. Son una poderosa herramienta funcional. En lugar de escribir for i in 0..vec.len(), aprende a usar .iter(), .map(), .filter(), .fold().
// Python: [x*2 for x in list if x > 0]\n// Rust:\nlet result: Vec<i32> = list.iter()\n .filter(|&x| *x > 0)\n .map(|x| x * 2)\n .collect();\n\nEste código no solo es más corto, sino que también funciona más rápido, ya que los iteradores son perezosos y el compilador los optimiza en un bucle lineal.
\n\n2. HashMap y BTreeMap
\nPara problemas de conteo de frecuencias (por ejemplo, "Two Sum" o "Group Anagrams"), HashMap es tu mejor amigo. Pero recuerda: en Rust, insertar en un HashMap requiere la propiedad de la clave. Si usas &str, tendrás que clonar la cadena o usar String.
use std::collections::HashMap;\n\nfn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {\n let mut map = HashMap::new();\n for (i, &num) in nums.iter().enumerate() {\n let complement = target - num;\n if let Some(&j) = map.get(&complement) {\n return vec![j as i32, i as i32];\n }\n map.insert(num, i);\n }\n vec![]\n}\n\nObserva: enumerate() devuelve una tupla (usize, &i32). Usamos &num para desreferenciar la referencia.
3. Vec y slices
\nVec es un arreglo dinámico. Pero en los problemas de LeetCode a menudo necesitas trabajar con subarreglos. Para eso, usa slices &[T]. No requieren copia y permiten pasar una parte del arreglo a una función.
fn max_subarray_sum(arr: &[i32]) -> i32 {\n let mut max_ending = arr[0];\n let mut max_so_far = arr[0];\n for &x in arr[1..].iter() {\n max_ending = std::cmp::max(x, max_ending + x);\n max_so_far = std::cmp::max(max_so_far, max_ending);\n }\n max_so_far\n}\n\n