Rust on LeetCode: Transitioning from Python to Systems Thinking
\n\nIf you've come to PythonLib, you're probably already familiar with Python. You know how to write concise one-liner solutions and take dynamic typing for granted. But the world doesn't stand still. More and more often, top companies (and not only them) ask candidates to write code in Rust during interviews. Why? Because Rust is a language that combines the speed of C with memory safety, and LeetCode is the perfect training ground to hone this skill.
\n\nIn this article, we won't just skim through the syntax. We'll break down how to think within the Rust paradigm when solving algorithmic problems, which data structures to use, and how not to drown in the borrow checker. Let's go!
Why LeetCode in Rust is Both a Challenge and an Advantage?
\n\nMost solutions on LeetCode are written in Python or Java. Rust, however, forces you to think about memory, ownership, and lifetimes. It may seem difficult, but that's precisely what makes you a stronger engineer. By solving problems in Rust, you automatically learn:
\n\n- \n
- Ownership — the language's main feature. \n
- Borrowing and mutable/immutable references. \n
- Efficient iterators without unnecessary allocations. \n
- Working with
OptionandResultinstead of exceptions. \n
If you come from Python, the first 10 problems will feel like torture. But once you understand how the compiler protects you from null pointer and data races, you'll fall in love with this language.
What to Learn Before You Start?
\n\nBefore diving into the pool of problems, let's go over the toolkit. Without it, you'll be writing code like C++ with macros — inefficient and painful.
\n\n1. Iterators and Closures
\nIn Rust, iterators are not just a for loop. They are a powerful functional tool. Instead of writing for i in 0..vec.len(), learn to use .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\nThis code is not only shorter but also faster, because iterators are lazy and the compiler optimizes them into a linear loop.
\n\n2. HashMap and BTreeMap
\nFor frequency counting problems (e.g., "Two Sum" or "Group Anagrams"), HashMap is your best friend. But remember: inserting into a HashMap in Rust requires ownership of the key. If you use &str, you'll need to clone the string or use 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\nNote: enumerate() returns a tuple (usize, &i32). We use &num to dereference the reference.
3. Vec and Slices
\nVec is a dynamic array. But in LeetCode problems, you often need to work with subarrays. For this, use slices &[T]. They don't require copying and allow you to pass a portion of the array to a function.
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