I started learning Rust today and figured, I could ask for some code reviews to learn to make my code idiomatic and learn about its code generation. I spend my days writing highly optimized Java and C++, so interested in Rust from that perspective.
This is the first problem from leetcode:
// Sorts a vector, so that even numbers appear first.
fn sort_array_by_parity(nums: Vec<i32>) -> Vec<i32> {
let mut res = nums.clone();
let mut even = 0; // index of first element of unknown parity
for (i, n) in nums.iter().enumerate() {
if n % 2 == 0 {
res.swap(even, i);
even = even + 1;
}
}
res
}
The code takes as input a list of numbers and sorts them so that even numbers are first, i.e. [3,2,4,1] -> [2,4,3,1]. I'm using a pointer into the list keeping track of a boundary on which we have on the left side stuff that is known to be even (similar to the typical implement of the partition function in quicksort).
I'm interested in a few things:
- Is there a more idiomatic way of writing the loop?
- Is there a more efficient way of writing the equivalent code?
For (2), so I was mainly wondering if there are ways to make Rust's compiler figure out that it doesn't have to make a range check on every access, but can instead reason that it only needs to check the bound once outside a loop. The generated assembly code shown by compiler explorer shows that it's not smart enough to avoid bounds checks on every iteration, i.e. the main loop generates the following code:
.LBB1_13:
testb $1, (%r12,%rax,4)
jne .LBB1_23
cmpq %r13, %rdi
jae .LBB1_21
cmpq %r13, %rax
jb .LBB1_22
leaq .L__unnamed_1(%rip), %rdx
movq %rax, %rdi
movq %r13, %rsi
callq *core::panicking::panic_bounds_check@GOTPCREL(%rip)
jmp .LBB1_2