The difference is night and day, I'm dismayed at how horrible that C++ code is, hope I never have to deal with such crap.
The difference is night and day, I'm dismayed at how horrible that C++ code is, hope I never have to deal with such crap.
I mean, try getting the same kind of performance and static verification in Python?
So much of the complexity of C++ is due to the static typing and the optimizations that can come with it. If you're willing to give up and make everything dynamic then of course it'll simplify your code, but you get a massive penalty both in terms of performance and in terms of error-checking.
[1] http://softwaremaniacs.org/blog/2016/02/12/ownership-borrowi...
In the general case that's theoretically impossible, and also not really a good measure anyway.
[1] https://rust-lang-nursery.github.io/edition-guide/rust-2018/...
https://play.rust-lang.org/?version=stable&mode=debug&editio...
While the `entry` API provided an ergonomic, more performant way to do this, this is an example of a common pattern one might expect from other languages that, until recently, did not compile.
NLL and other ergonomics improvements have helped a lot with these paper-cuts.
*example_map.entry("some_key").or_insert(0) += 1;
Or, if you prefer an extra intermediate for clarity: let entry = example_map.entry("some_key").or_insert(0);
*entry += 1;
This is a good example of how, often, Rust’s approach of having greater underlying complexity but exposing it sanely ends up allowing you to write something that is semantically superior and easier to reason about, while being faster too.This and Rust’s ownership model (which I declare stands under the same banner) is the sort of thing that I regularly miss when working in JavaScript (and regularly missed when I used to work in Python plenty).
The Pin<> type constructor is being standardized to support this case. This is not so much "custom move semantics" as a lack of in-built "move" semantics of any sorts, but the basic use case is being worked on.
> And does the compiler no longer complain about code that really should work just fine?
As other people have mentioned, the NLL feature addresses many cases where this used to be a problem, and has now been introduced to the 'stable' version.
In Python it's blindingly obvious what is happening: one is creating a group of 3 values, where the values are in specific ranges if a certain condition is met. Itertools.count() is perhaps opaque, but otherwise this is literally how the code reads!
Rust is already going into the weeds after the first '=' with its flat_maps and (1..) = c, whatever the heck that means and the C++ code is not even worth talking about.
Performance is not an excuse, since loops can solve this with good performance and probably the best readability.
The 1..=c is Rust syntax for an inclusive range. Rust also has syntax for ranges that exclude the last value like Python’s range(1, c + 1), which is 1..(c + 1). Using that would make it look more like the other examples, though since the language has syntax for the idea “1 to c inclusive”, it makes sense to just use it.
For comparison Ruby uses a..z and a...z for inclusive and exclusive ranges respectively. I like that Rust’s inclusive range is more visually distinct, and I can see why they went for a symbol that implies the last element “equals” another value, but it does look unusual at first glance.
In P6, 1..10 is exactly the range you'd expect, while ^ is used to exclude either end of the range. For example, 1..^11 is the same as 1..10, as is 0^..^11 (can't say I've ever needed that, but I could see a couple of possible uses).
This also carries over into range syntactic sugar. ^10 is shorthand for 0..^10, and as a result ^@list.elems is a list of valid indices of a list.
I personally find Rust's syntax noisier, but it's a small issue.
That syntax is impressive, handles all four possibilities nicely.
However since reading this short article by Dijkstra, I’ve been convinced that 0..N ranges (inclusive start, exclusive end) are the way to go whenever there’s a choice:
http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF
There's certainly a mental benefit of having made a choice and being consistent, which I think is possible with any of the options, but I find the argument of having length = end - start compelling.
It does fall down in some cases, eg when looking for a range over all unsigned ints, as the total number of uints isn’t a uint itself (as uints start at zero), so some way to represent a rage that is inclusive of both ends is still needed.