Rust was designed by very smart people, who put a lot of thought into it. Blocks that evaluate to the last expression are a good idea. Move semantics by default are a great idea. Stealing type classes from Haskell and adapting them to create a sensible form of template metaprogramming is awesome. And borrowck is just flat-out Galaxy Brain territory.
Rust was designed by very smart people, and most of their choices make sense after you think about it. Of course
if let has to be a completely different construct than
let isn’t an expression, and
let can’t be an expression because expressions don’t introduce new variables. Of course you can’t return a DST, since that would require functions to
alloca() into their parent’s stack frame, which is not possible with a pure stack data structure. Of course
for <'a, 'b> Fn(&'a T, &'b U) -> &'a usize is a thing, since you have to be able to declare a type for
fn foo<'a, 'b>(&'a T, &'b U) -> &'a usize, and if the angle brackets were attached to the
Fn, then you’d never be able to stablize direct use of the
Fn trait. And don’t even get me started on the turbofish.
But they are confusing, and they feel arbitrary because the only reason they are the way they are is because of other design choices. The confusing corner-cases surrounding
if let could have been avoided by allowing expressions to introduce locals, so
let would actually be an expression. The turbofish, and the weird-looking
for<> syntax, could have been avoided by making other changes to the syntax.
The hard stuff is Rust’s Top. Rust’s big Flop is that it also includes confusing stuff, which, as Joe Armstrong put it, “you have to explain to people over and over again.”