The "last expression of the function" does not literally mean the last line only. If the last expression happens to be a structured expression like if or match, it has a value too, so you can omit the returns there as well.
I think it’s fine that you can omit them but should clippy enforce this?
I leave them in because I think it’s clearer if there is code in between a bunch of other code that does a return, to mark it explicitly with the keyword. Especially if you have a complicated nested setup (which of course you shouldn’t have).
Regarding your direct question, I do think clippy should enforce it. Rust is an expression based language, which is different from what a lot of people are used to. Every block evaluates to the value of its last statement. Every function returns the value our evaluates to. It's a powerful pattern, and worth teaching through clippy.
If you have a strong desire to write non idiomatic rust, feel free to disable the lint, but idiomatic rust only uses return when necessary.
Note, it's almost wrong in terms of rust semantics to even think about it as "omitting" a return statement. Explicitly adding the return is as strange in rust as writing
def main():
import sys
sys.exit(0)
return
in Python. It's redundant, and leads the reader to suspect something weird is happening when it's not.
Note that if you look at the core of Go To Statement Considered Harmful, it's about
The unbridled use of the go to statement has an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress.
The one use of goto that doesn't have this problem is using it to exit a block -- and that's exactly what break from loops (including labeled loops), and return do.
That's why clippy doesn't complain about all early returns, as was mentioned above -- only the ones that are unneeded, the same way the compiler complains about unnecessary parentheses.