Omitting the return keyword

I’m gonna be honest, I never liked the idiomatic way of writing return values:

fn foo() -> u32 {
  42
}

I’ve tried the past few months to get used to it, but unsuccessfully so. I do it anyway because it’s idiomatic. I think one thing that makes it so hard for me to swallow is that it feels inconsistent to me. I expect to be able to end every scope the same way, but this – for instance – doesn’t work:

fn maybe_lopt(arg: &str) -> bool {
  if arg.len() > 2 && arg.starts_with("--") == true {
    true
  }
  false
}

… so I need to do this instead:

fn maybe_lopt(arg: &str) -> bool {
  if arg.len() > 2 && arg.starts_with("--") == true {
    return true;
  }
  false
}

I thought the rule was simply that omitting return and ‘;’ is only allowed at the very bottom of a function. But this turns out not to be entirely true either; I’ve randomly tried to remove them here and there and in some inner conditional scopes it works and most others it doesn’t.

I’m thinking that I may learn to live with it if I understood what the rules are. So … what are they? :slight_smile:

return is for early returns.


The rule is that a block evaluates to an expression if the final expression in the block evaluates to one (and has no semicolon).[^1] This is true not just for functions, but any block:

let vec = {
    let mut buf = vec![];
    buf.push(3);
    buf
};

If you have a function like

fn boole(b: bool) -> i32 {
    if b {
        1
    } else {
        0
    }
}

that works because if...else is an expression. (as is if let...else and match). Maybe you might even like to format it as

fn boole(b: bool) -> i32 {
    if b { 1 } else { 0 }
}

[^1]: To be pedantic, all blocks evaluate to an expression, but those with a trailing semicolon evaluate to ().

3 Likes

You can omit the semicolon at the bottom of any block, but some blocks like for if, while, and for are not guaranteed to run to they must return ().

But, if else, match, and bare blocks can return any value, so you can do

fn maybe_lopt(arg: &str) -> bool {
    if arg.len() > 2 && arg.starts_with("--") == true {
        true
    } else {
        false
    }
}

Or even better

fn maybe_lopt(arg: &str) -> bool {
    arg.len() > 2 && arg.starts_with("--")
}
4 Likes

Just a note that because expressions return implicitly and expressions evaluate to a value, this function can be written as:

fn maybe_lopt(arg: &str) -> bool { 
    arg.len() > 2 && arg.starts_with("--")
}

Not sure if you find this to be easier or harder to read :slight_smile:

3 Likes