Skip to main content
edited tags; edited title
Link
200_success
  • 144.2k
  • 22
  • 188
  • 473

Idiomatic Rust loops Partitioning a list so that even numbers appear before odd numbers

Source Link
eof
  • 193
  • 1
  • 4

Idiomatic Rust loops

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:

  1. Is there a more idiomatic way of writing the loop?
  2. 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