4 ms·
I think one thing that confuses people about Rust is it sends mixed signals with regard to functional vs imperative. It gives you lots of nice functional toys -
by _bxg1 7y ago
I think one thing that confuses people about Rust is it sends mixed signals with regard to functional vs imperative. It gives you lots of nice functional toys - closures, implicit returns, pattern matching - but you end up having to write a lot of imperative stuff too.
I've found that Rust flows much more nicely when you just embrace an imperative style "by default". That's its native tongue; the functional stuff is more of an exception.
Those of us used to multi-paradigm languages have taught ourselves that imperative == bad (actual for-loops! egads!), but the thing is, Rust is specifically designed to make it not-bad. It gives you all these tools to carefully control and limit mutability, so you can do imperative stuff with much less fear. It takes getting used to but in my (limited) experience this mindset significantly reduces friction when working with Rust.
- skohan 7y agoI haven't spent a lot of time with FP languages over the years, but implicit returns are one of my least favorite features of Rust. Especially the use of semicolons to denote a terminated statement vs. a return value: a semicolon-related typo is such an easy mistake to make, and an annoying reason for a build to fail, especially when so many modern languages have shown it's quite possible and pleasant to work without semicolons.
- steveklabnik 7y agoTo be clear, Rust does not have "implicit returns" nor does it use semicolons to indicate return values. Expressions evaluate to a value, and semicolons take an expression, throw away its value, and evaluate to () instead. That's it. The other behaviors you're talking about fall out of these semantics, but are also different than what you've said; functions evaluate to a value, so the final expression's value determines the value it evaluates to, but you cannot "implicitly return" from the middle of a function by dropping a ;, for example.
- _bxg1 7y agoWhat I meant was the fact that you can omit the "return" keyword: fn foo(x: i32) -> i32 { x * 2 }
- steveklabnik 7y agoRight, but only as the last expression in a block, which is different than what "implicit returns" implies.
- skohan 7y agoIt's a bit of a semantic distinction isn't it? In practice what it means is that you add semicolons to the end of almost every line of imperative code in a block, and you leave the semicolon off the last line and this becomes your return value implicitly (without the use of a return keyword).
- steveklabnik 7y agoIt is a semantic distinction, because "everything is an expression" and "implicit returns" have different semantics. Thinking of them as implicit returns leads people to believe they can write code like fn foo(x: bool) -> i32 { if x { 5 } // more code and then they're confused when this doesn't work.
- _bxg1 7y agoI actually love the semicolon semantics. Making expressions and statements disjoint prevents bugs like this: if(foo = 5) { ... } They are easy to mess up when you're getting started, but they're also very easy to fix because they'll basically always result in a clear compiler error. If "builds failing" are annoying for you, you probably aren't using an IDE with inline errors, and with Rust you absolutely should be doing that or you'll have a bad time.
- skohan 7y agoif(foo = 5) { ... } You don't need the rust style expression/statement distinction to catch this. Many languages will catch this at compile time since "foo = 5" does not evaluate to a boolean. I know that it's relatively easy to catch this error in Rust, and it is at most a minor annoyance, but it still feels like a clear step backwards after working with languages which don't require semicolons at all.
- _bxg1 7y ago> since "foo = 5" does not evaluate to a boolean In most C-like languages, this expression is valid: the whole thing evaluates to "5", which has a truthiness. Some "clever" programmers use this pattern intentionally in loops to save a line of code, especially in the C/C++ world. For example: while(currentNode = currentNode.next) { ... } When currentNode becomes null, the expression/statement becomes falsy, and the loop terminates. I absolutely love the way Rust systematically disallows shaky syntax tricks like this.