Rust no LeetCode: Transição do Python para o pensamento sistêmico
\n\nSe você veio para o PythonLib, provavelmente já conhece Python. Você sabe escrever soluções concisas em uma linha e usa a tipagem dinâmica como algo natural. Mas o mundo não para. Cada vez mais, em entrevistas para empresas de ponta (e não só), pedem para escrever código em Rust. Por quê? Porque Rust é uma linguagem que combina a velocidade do C com a segurança de memória, e o LeetCode é o campo de treinamento ideal para aperfeiçoar essa habilidade.
\n\nNeste artigo, não vamos apenas passar pela sintaxe. Vamos analisar como pensar no paradigma do Rust ao resolver problemas algorítmicos, quais estruturas de dados usar e como não se afogar no borrow checker. Vamos nessa!
Por que LeetCode em Rust é um desafio e uma vantagem?
\n\nA maioria das soluções no LeetCode é escrita em Python ou Java. Rust, por outro lado, faz você pensar sobre memória, propriedade e tempo de vida. Parece difícil, mas é exatamente isso que o torna um engenheiro mais forte. Ao resolver problemas em Rust, você automaticamente aprende:
\n\n- \n
- Sistema de Propriedade (Ownership) — a principal característica da linguagem. \n
- Empréstimo (Borrowing) e referências mutáveis/imutáveis. \n
- Iteradores eficientes sem alocações desnecessárias. \n
- Trabalho com
OptioneResultem vez de exceções. \n
Se você veio do Python, os primeiros 10 problemas parecerão uma tortura. Mas quando você entender como o compilador o protege de null pointer e condições de corrida, vai se apaixonar por essa linguagem.
O que estudar antes de começar?
\n\nAntes de se jogar nos problemas, vamos analisar o kit de ferramentas. Sem ele, você escreverá código como se fosse C++ com macros — ineficiente e doloroso.
\n\n1. Iteradores e Closures
\nEm Rust, iteradores não são apenas um loop for. São uma ferramenta funcional poderosa. Em vez de escrever for i in 0..vec.len(), aprenda 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 não é apenas mais curto, mas também funciona mais rápido, pois os iteradores são preguiçosos e o compilador os otimiza em um loop linear.
\n\n2. HashMap e BTreeMap
\nPara problemas de contagem de frequências (por exemplo, "Two Sum" ou "Group Anagrams"), HashMap é seu melhor amigo. Mas lembre-se: em Rust, inserir em um HashMap exige a propriedade da chave. Se você usar &str, precisará clonar a string ou 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\nObserve: enumerate() retorna uma tupla (usize, &i32). Usamos &num para desreferenciar a referência.
3. Vec e slices
\nVec é um array dinâmico. Mas nos problemas do LeetCode, muitas vezes é necessário trabalhar com subarrays. Para isso, use slices &[T]. Eles não exigem cópia e permitem passar parte do array para uma função.
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