Rust sur LeetCode : Passer de Python à une pensée système
\n\nSi vous êtes sur PythonLib, vous connaissez probablement déjà Python. Vous savez écrire des solutions concises en une seule ligne et considérez le typage dynamique comme une évidence. Mais le monde ne s'arrête pas là. De plus en plus souvent, lors des entretiens dans les meilleures entreprises (et pas seulement), on demande d'écrire du code en Rust. Pourquoi ? Parce que Rust est un langage qui allie la vitesse du C à la sécurité mémoire, et LeetCode est un terrain d'entraînement idéal pour perfectionner cette maîtrise.
\n\nDans cet article, nous ne nous contenterons pas de survoler la syntaxe. Nous analyserons comment penser dans le paradigme Rust lors de la résolution de problèmes algorithmiques, quelles structures de données utiliser, et comment ne pas se noyer dans le borrow checker. C'est parti !
Pourquoi LeetCode en Rust est un défi et un avantage ?
\n\nLa plupart des solutions sur LeetCode sont écrites en Python ou Java. Rust, quant à lui, vous oblige à réfléchir à la mémoire, à la propriété et à la durée de vie. Cela semble difficile, mais c'est précisément ce qui vous rend plus fort en tant qu'ingénieur. En résolvant des problèmes en Rust, vous apprenez automatiquement :
\n\n- \n
- Le système de propriété (Ownership) — la principale caractéristique du langage. \n
- L'emprunt (Borrowing) et les références mutables/immutables. \n
- Les itérateurs efficaces sans allocations inutiles. \n
- Le travail avec
OptionetResultau lieu des exceptions. \n
Si vous venez de Python, les 10 premiers problèmes vous sembleront une torture. Mais une fois que vous comprendrez comment le compilateur vous protège des null pointer et des conditions de course, vous tomberez amoureux de ce langage.
Que faut-il apprendre avant de commencer ?
\n\nAvant de vous lancer dans les problèmes, examinons la boîte à outils. Sans elle, vous écrirez du code comme en C++ avec des macros — inefficace et douloureux.
\n\n1. Itérateurs et fermetures (Closures)
\nEn Rust, les itérateurs ne sont pas qu'une simple boucle for. C'est un outil fonctionnel puissant. Au lieu d'écrire for i in 0..vec.len(), apprenez à utiliser .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\nCe code est non seulement plus court, mais aussi plus rapide, car les itérateurs sont paresseux et le compilateur les optimise en une boucle linéaire.
\n\n2. HashMap et BTreeMap
\nPour les problèmes de comptage de fréquences (par exemple, "Two Sum" ou "Group Anagrams"), HashMap est votre meilleur ami. Mais rappelez-vous : en Rust, l'insertion dans un HashMap nécessite la propriété de la clé. Si vous utilisez &str, vous devrez cloner la chaîne ou utiliser 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\nNotez que enumerate() retourne un tuple (usize, &i32). Nous utilisons &num pour déréférencer la référence.
3. Vec et les slices
\nVec est un tableau dynamique. Mais dans les problèmes LeetCode, on doit souvent travailler avec des sous-tableaux. Pour cela, utilisez les slices &[T]. Elles ne nécessitent pas de copie et permettent de passer une partie du tableau à une fonction.
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