1

I'm usually using return foo; at the end of a function because it I find this easier to understand. But I'm not sure if this is really a good idea because the "normal" return expression is the one without a semicolon. Should I use the return without the semicolon or not (for example for better performance)?

Example:

//My typical functions
pub fn user_exist(&self, key: &str) -> Result<(), ApiError> {
    let mut cache: PooledConnection<Client> = connection()?;

    cache.exists(key)?;
    return Ok(());
}

// Is it better to use this?
pub fn user_exist(&self, key: &str) -> Result<(), ApiError> {
    let mut cache: PooledConnection<Client> = connection()?;

    cache.exists(key)?;
    Ok(())
}
Jan
  • 93
  • 9
  • 3
    Consider this: since the behavior is the same, if the performance _was_ somehow different, the compile could just compiler the slower variant as-if you wrote the faster variant – Brian61354270 Mar 06 '23 at 14:50

1 Answers1

1

The compiler doesn't make a distinction between the two, they are completely equivalent. Consider the following two examples:


pub fn square1(x: usize) -> usize {
    x*x
}

pub fn square2(x: usize) -> usize {
    return x*x;
}

Looking at the assembly in the godbolt compiler explorer, we get identical code:

example::square1:
        mov     rax, rdi
        imul    rax, rdi
        ret

example::square2:
        mov     rax, rdi
        imul    rax, rdi
        ret

From a style point of view, it is convention to not use return statements unless doing an early return, see this question for more details.

edit: @Chayim Friedman brought up a good point that looking at the assembly isn't hard proof they are equivalent. Although again not hard proof, looking at the MIR for these two functions gives further evidence the compiler is treating the two returns the same:

fn square1(_1: usize) -> usize {
    debug x => _1;                       // in scope 0 at src/lib.rs:1:16: 1:17
    let mut _0: usize;                   // return place in scope 0 at src/lib.rs:1:29: 1:34
    let mut _2: usize;                   // in scope 0 at src/lib.rs:2:5: 2:6
    let mut _3: usize;                   // in scope 0 at src/lib.rs:2:7: 2:8
    let mut _4: (usize, bool);           // in scope 0 at src/lib.rs:2:5: 2:8

    bb0: {
        _2 = _1;                         // scope 0 at src/lib.rs:2:5: 2:6
        _3 = _1;                         // scope 0 at src/lib.rs:2:7: 2:8
        _4 = CheckedMul(_2, _3);         // scope 0 at src/lib.rs:2:5: 2:8
        assert(!move (_4.1: bool), "attempt to compute `{} * {}`, which would overflow", move _2, move _3) -> bb1; // scope 0 at src/lib.rs:2:5: 2:8
    }

    bb1: {
        _0 = move (_4.0: usize);         // scope 0 at src/lib.rs:2:5: 2:8
        return;                          // scope 0 at src/lib.rs:3:2: 3:2
    }
}

fn square2(_1: usize) -> usize {
    debug x => _1;                       // in scope 0 at src/lib.rs:5:16: 5:17
    let mut _0: usize;                   // return place in scope 0 at src/lib.rs:5:29: 5:34
    let mut _2: usize;                   // in scope 0 at src/lib.rs:6:12: 6:13
    let mut _3: usize;                   // in scope 0 at src/lib.rs:6:14: 6:15
    let mut _4: (usize, bool);           // in scope 0 at src/lib.rs:6:12: 6:15

    bb0: {
        _2 = _1;                         // scope 0 at src/lib.rs:6:12: 6:13
        _3 = _1;                         // scope 0 at src/lib.rs:6:14: 6:15
        _4 = CheckedMul(_2, _3);         // scope 0 at src/lib.rs:6:12: 6:15
        assert(!move (_4.1: bool), "attempt to compute `{} * {}`, which would overflow", move _2, move _3) -> bb1; // scope 0 at src/lib.rs:6:12: 6:15
    }

    bb1: {
        _0 = move (_4.0: usize);         // scope 0 at src/lib.rs:6:12: 6:15
        return;                          // scope 0 at src/lib.rs:7:2: 7:2
    }
}
effect
  • 1,279
  • 6
  • 13
  • Thanks! The assembly code is really helpful. – Jan Mar 07 '23 at 05:47
  • 2
    @Jan Looking at the assembly is not really a proof in this case, because it could be that one is slower and it just happens that the compiler managed to optimize it, but it could fail in other scenarios. – Chayim Friedman Mar 07 '23 at 09:53
  • Good point, it's not hard proof, just some evidence to support. – effect Mar 07 '23 at 17:18