Search code examples
loopsrustclosurescompiler-optimization

Does Rust detects duplicated closures (eg. ones defined in loop)?


When I need to call many failable functions, I sometimes put them into closure. Then I ? them and catch any special variant (exception mechanism in other languages).

The following is my solution to https://www.codewars.com/kata/5a090c4e697598d0b9000004/train/rust .

use std::collections::VecDeque;

fn solve(nums: &[i32]) -> Vec<i32> {
    let mut sorted = VecDeque::from_iter(nums.iter().copied());
    sorted.make_contiguous().sort();

    let mut rearranged = vec![];
    while let Some(()) =
        (||->Option<()>{
            rearranged.push(sorted.pop_back()?);
            rearranged.push(sorted.pop_front()?);
            Some(())
        })()
    {}
    rearranged
}

Works fine and is clean, but I wonder, “Does Rust produce a closure instance on every while loop iteration”? (EDIT: I added the word instance) If so, does it detect that produced closures are “just duplicates” of the first one and optimizes duplication away?

I would be grateful for some theoretical consideration of cases when closure is defined inside the loop, like above. What optimizations compiler does to avoid duplication of data?


Solution

  • The easiest way to check is to have a look at the generated assembly, of course using --release.

    Your code:

    solve:
        pushq   %rbp
        pushq   %r15
        pushq   %r14
        pushq   %r13
        pushq   %r12
        pushq   %rbx
        subq    $200, %rsp
        movq    %rdi, %r8
        testq   %rdx, %rdx
        je  .LBB6_6
        movq    %rdx, %rbx
        movq    %r8, 128(%rsp)
        xorl    %ebp, %ebp
        movq    %rdx, %rax
        shrq    $61, %rax
        sete    %al
        jne .LBB6_128
        movq    %rsi, %r14
        leaq    (,%rbx,4), %rdi
        movb    %al, %bpl
        shlq    $2, %rbp
        movq    %rdi, 104(%rsp)
        movq    %rbp, %rsi
        callq   *__rust_alloc@GOTPCREL(%rip)
        testq   %rax, %rax
        je  .LBB6_129
        movq    %rbx, 40(%rsp)
        leaq    (%r14,%rbx,4), %r12
        addq    $-4, %r12
        movq    %r12, %rbp
        subq    %r14, %rbp
        movq    %rbp, %rbx
        shrq    $2, %rbx
        leaq    4(%rbp), %rdx
        andq    $-4, %rdx
        movq    %rax, (%rsp)
        movq    %rax, %rdi
        movq    %r14, %rsi
        callq   *memcpy@GOTPCREL(%rip)
        leaq    1(%rbx), %rax
        movq    %rax, 16(%rsp)
        cmpq    $80, %rbp
        jae .LBB6_7
        movl    $1, %r15d
        cmpq    %r14, %r12
        movq    (%rsp), %r12
        je  .LBB6_106
        movq    16(%rsp), %rax
        leaq    (%r12,%rax,4), %r8
        addq    $-4, %r8
        leaq    (%r12,%rbx,4), %rcx
        addq    $4, %rcx
        movq    $-2, %rbp
        jmp .LBB6_97
    

    Defining the closure in a variable (playground):

    solve:
        pushq   %rbp
        pushq   %r15
        pushq   %r14
        pushq   %r13
        pushq   %r12
        pushq   %rbx
        subq    $200, %rsp
        movq    %rdi, %r8
        testq   %rdx, %rdx
        je  .LBB6_6
        movq    %rdx, %rbx
        movq    %r8, 128(%rsp)
        xorl    %ebp, %ebp
        movq    %rdx, %rax
        shrq    $61, %rax
        sete    %al
        jne .LBB6_128
        movq    %rsi, %r14
        leaq    (,%rbx,4), %rdi
        movb    %al, %bpl
        shlq    $2, %rbp
        movq    %rdi, 104(%rsp)
        movq    %rbp, %rsi
        callq   *__rust_alloc@GOTPCREL(%rip)
        testq   %rax, %rax
        je  .LBB6_129
        movq    %rbx, 40(%rsp)
        leaq    (%r14,%rbx,4), %r12
        addq    $-4, %r12
        movq    %r12, %rbp
        subq    %r14, %rbp
        movq    %rbp, %rbx
        shrq    $2, %rbx
        leaq    4(%rbp), %rdx
        andq    $-4, %rdx
        movq    %rax, (%rsp)
        movq    %rax, %rdi
        movq    %r14, %rsi
        callq   *memcpy@GOTPCREL(%rip)
        leaq    1(%rbx), %rax
        movq    %rax, 16(%rsp)
        cmpq    $80, %rbp
        jae .LBB6_7
        movl    $1, %r15d
        cmpq    %r14, %r12
        movq    (%rsp), %r12
        je  .LBB6_106
        movq    16(%rsp), %rax
        leaq    (%r12,%rax,4), %r8
        addq    $-4, %r8
        leaq    (%r12,%rbx,4), %rcx
        addq    $4, %rcx
        movq    $-2, %rbp
        jmp .LBB6_97    
    

    They're identical.