6 ms·
Rust for C++ programmers – part 2: control flow
- dcsommer 13y agoAnother great entry. Keep 'em coming!
- shmerl 13y agoWhat is the reason for using overly minimalistic, assembly style keywords like fn, str etc? Even C++ doesn't go that far (I think string is more pleasant to read than str). It's not like it's saving much, and it only worsens readability.
- nrc 13y agoIt was a design decision made long ago and seems to have stuck (although is slightly less extreme than it used to be). I also used to hate the abbreviation, but I find you get used to it quickly and after a few weeks it doesn't impact readability at all.
- jfager 13y agoIt's a matter of taste. I personally feel that your specific examples make Rust more readable, because of how little space gets wasted on those frequent, unambiguous names. Ubiquitous boilerplete-ish things that show up everywhere should recede a bit, imo; let me spend my line width budget on what's unique and important about my code. But again, it's just a matter of taste and not really worth wasting a lot of time worrying about. The important thing about Rust is its semantics (speed, safety, control), not its syntax.
- bjz_ 13y ago> The important thing about Rust is its semantics (speed, safety, control), not its syntax. Agreed. I love Haskell syntax above all else, but I like Rust's semantics too much to let that get in the way of me using it.
- shmerl 13y agoSure, I didn't want to sound dismissive, I'm very interested in Rust semantics.
- Derbasti 13y agoif `str` is too short, surely `int` is, too: function fibunacci(integer index) { if (index <= 1) { return 1; } else { return fibunacci(index-1) + fibunacci(index-2); } } compared to: fn fib(int n) { if (n <= 1) { return 1; } else { return fib(n-1) + fib(n-2); } } Personally, I prefer `fn`, `int`, and `str`. I actually think that the short names are a nice way of distinguishing between built-in keywords (abbreviated, like `str`), and user-defined names (typically whole words). Also, short names are useful since they can be used as pre/post-fixes to function names like `strsplit`.
- gmjosack 13y agoyour comparison isn't fair. you're making an argument against fn/int vs function/integer but the largest difference in the code is between index and n. I prefer the version with a longer variable name despite the length. also shouldn't the returns be unnecessary here since the conditional is an expression?
- oscargrouch 13y agoi think 'fn' is too much minimalism.. better 'fun' or even 'func' .. but maybe they wanted to distinguish the sintax from Go's 'func' int -> int (ok) str -> string but anyway.. nothing that can prevent someone to enjoy the language.. but the 'fn' for me is the most odd.. getting used to it
- bjz_ 13y agoErgonomics. More commonly used things should be shorter and easier to type. Less used, potentially dangerous stuff should be longer - `unsafe` for example. It looks weird at first, but you quickly get used to it.
- alkonaut 13y agoCompletely agree. Some language constructs that are "almost symbols" such as fn certainly works well to abbreviate, but for function names and types it feels outdated and hard to read. If a common function name is too long to spell out like iterate() then that is a candidate for sugaring out completely, not cutting in half. Even in good C codebases that run static analysis, there are errors due to mixing up "memcpy()" and "memcmp()". UT4 is one example where this happened.
- pcwalton 13y ago> If a common function name is too long to spell out like iterate() then that is a candidate for sugaring out completely, not cutting in half. We can't sugar it out completely because there are multiple types of iterators: ".mut_iter()" and ".move_iter()" for example just on arrays. > Even in good C codebases that run static analysis, there are errors due to mixing up "memcpy()" and "memcmp()". UT4 is one example where this happened. Well, that particular error would be unlikely to happen in Rust because the return types are different, and there aren't the implicit coercions that there are in C.
- Alphasite_ 13y agoI'm perhaps missing the issue completely, but isn't the argument about sensible defaults?
- stormbrew 13y agoOn this syntactic readability level I find the semicolons, in terms of both the fact that they are usually necessary and that the final semicolon in a statement group has meaning based on whether or not it's present, much worse than any terse keywords. If anything the terseness of keywords seems almost necessary because rust expressions seem to tend towards the wordy whenever type annotations come into play, and I'm just glad it didn't devolve into haskell-style symbolics for everything.
- logicchains 13y agoWhy is the symbol '|' used for or in pattern matching, not '||'? If '|' is used for bitwise or elsewhere in the language then this might lead to confusion.
- adamnemecek 13y agoIt was probably inherited from ML.
- pcwalton 13y agoML and Scala. Haskell uses a semicolon, but that wouldn't work in Rust's grammar.
- masklinn 13y ago> If '|' is used for bitwise or elsewhere in the language then this might lead to confusion. `||` is the logical or[0], why would it lead to less confusion? (and yes, `|` is the bitwise or[1]) [0] http://static.rust-lang.org/doc/0.10/rust.html#lazy-boolean-operators http://static.rust-lang.org/doc/0.10/rust.html#lazy-boolean-... [1] http://static.rust-lang.org/doc/0.10/rust.html#bitwise-operators http://static.rust-lang.org/doc/0.10/rust.html#bitwise-opera...
- logicchains 13y agoI suppose I just assumed that logical or is closer in meaning to the or in pattern matching than bitwise or is.
- masklinn 13y ago> If we want to index over the indices of `all` (a bit more like a standard C++ for loop over an array), you could do for i in range(0, all.len()) { println!("{}: {}", i, all.get(i)); } I'd suggest using `enumerate`[0] instead of creating a range on an existing collection: for (i, n) in all.iter().enumerate() { println!("{}: {}", i, n); } [0] http://static.rust-lang.org/doc/0.10/std/iter/struct.Enumerate.html#method.enumerate http://static.rust-lang.org/doc/0.10/std/iter/struct.Enumera...
- nrc 13y agoI actually used enumerate in an earlier version of this post, but in the case I used it didn't work (pointed out to me on /r/rust). I'm going to go into enumerate and other iterator functions in future posts, so left it out for now. I agree this is a better formulation.
- pcwalton 13y agoenumerate() also has the advantage of guaranteed elimination of bounds checks. In general, iterators are Rust's solution for achieving C-like performance for common patterns with safety and without bounds checking.
- masklinn 13y agoThat as well. By the way, I always wonder: is there a reason for no Iterable trait, which for could just take directly and implicitly call `.iter()` on? E.g. why for e in my_vec.iter() { but not for e in my_vec { ?
- pcwalton 13y agoYou'd have to overload "for" to detect whether the expression implemented Iterable or Iterator to decide what to do, and that was felt to be too complex (e.g. what if you implement both?) There was an attempt to have a unified Iterable trait at one point [1], but I believe it didn't work with the trait matching rules. [1]: https://github.com/mozilla/rust/issues/7597 https://github.com/mozilla/rust/issues/7597
- acqq 13y agoI still find it's a pity that so new language has "you must write let" that even Basic programmers don't have to do during at least 30-40 years: https://www.powerbasic.com/support/help/pbcc/let_statement.htm https://www.powerbasic.com/support/help/pbcc/let_statement.h... I remember not having to write let in Basic even in eighties. Anyway, since 1995, http://en.wikipedia.org/wiki/Limbo_(programming_language) http://en.wikipedia.org/wiki/Limbo_(programming_language) people have an idea that there is a simple way to have "define and assign" that is different from "assign" without requiring a keyword: use := for define and assign, x:=exp is equivalent to let x=exp, use = for just assign. Google Go just reused that idea. Even if it's used today by Go, I still don't understand why Rust avoids it, because it makes sources significantly more readable: the variable is not obstructed by the keyword. let mut is even more "stuff" before the variable in the language where declarations otherwise always follow the variable (which is good!) I'd use x := expression and x : mut = expression.
- pcwalton 13y agoIt wouldn't work in Rust because "let" supports arbitrary (irrefutable) patterns, and Rust has full pattern matching. The parser has to know when a pattern is coming up. Languages that use :=, like Go, don't have a complete set of destructuring features. The convenience of being able to have an arbitrary pattern in "let" bindings far outweighs the disadvantage of having to type "let". "x : mut = expression" wouldn't work in Rust because types follow :, and "mut" is not part of a type. "mut" is needed after each binding because one pattern can contain some mutable bindings and some immutable bindings.
- acqq 13y agoCan you please point me to the examples of the kind of the pattern matching constructs that don't allow using := ? I know it's obvious to you, I don't have your experience but I'd really want to understand. Thanks in advance. Regarding "mut is not a part of a type," so what? From my perspective (of somebody who implemented just much simpler languages than Rust) it's something like a "storage attribute." It doesn't prevent me to build the syntax to be more readable: it's a keyword anyway. It's certainly not that it can't be there because otherwise it's unparsable. Having x = expr, x := expr and x : mut = expr make all pretty clean and obvious to me...
- jhgg 13y agoWhat's the purpose of: y if y < 20 In the match block, can't we just use x?
- xixixao 13y agoThe two issues in the comments so far, iteration with index variable and assignment/destructuring without explicit let are both handled beautifully by CoffeeScript. At least Rust gets right "everything is an expression". One of the best things CoffeeScript "brought".
- cmrx64 13y agoAs if coffeescript originated the features... We don't have iteration with index variable because it's seen as extraneous and unnecessary. If you aren't using iterators, you're paying for unnecessary bounds checks (LLVM often can't optimize them out). The explicit let mostly comes from the ML heritage and want for nice pattern matching.