4 ms·
> but your Python example makes a complete mockery of it. I mean, try getting the same kind of performance and static verification in Python? So much of the c
by mehrdadn 8y ago
> but your Python example makes a complete mockery of it.
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.
- Sharlin 8y agoUpvoted, but to be fair, Rust shows that expressiveness and performance aren't mutually exclusive. It does not (currently) have language-level list comprehensions, but those could be easily enough implemented as simple syntactic transformations over the existing iterator combinators, the way Scala does it.
- mehrdadn 8y agoThanks, yeah, I was very careful to avoid claiming they're mutually exclusive. However, although I stopped trying to learn Rust back in 2016, unless it's changed its fundamentals radically (which I understand it hasn't), I'm not sure if it's really a counterexample here for expressiveness. Can you use different allocators for different vectors in the same function in Rust? Can you have custom move semantics for objects that might know about pointers to them? And does the compiler no longer complain about code that really should work just fine? [1] I understand the answers to most if not all such questions is still "no", at which point I stop believing it really has the same flexibility as C++ or the same ease of writing as Python, but would be happy to hear if things have changed. [1] http://softwaremaniacs.org/blog/2016/02/12/ownership-borrowing-hard/en/ http://softwaremaniacs.org/blog/2016/02/12/ownership-borrowi...
- couchand 8y ago> And does the compiler no longer complain about code that really should work just fine? In the general case that's theoretically impossible, and also not really a good measure anyway.
- mehrdadn 8y agoOkay, how about in the practical cases that actually come up and make you have to work around the compiler?
- Sharlin 8y agoNon-lexical lifetimes, which were just recently stabilized [1], make many of the most common cases of borrow checker frustration "just work". And now that the groundwork is done, even more cases will likely be supported in the future. [1] https://rust-lang-nursery.github.io/edition-guide/rust-2018/ownership-and-lifetimes/non-lexical-lifetimes.html https://rust-lang-nursery.github.io/edition-guide/rust-2018/...
- mehrdadn 8y agoConfused, how are these common use cases, and how is this "just working"? In both examples on that page there is an unused variable (seems rare to do intentionally?) and in the second case where one of them is used, it seems they just elaborated the error message.
- staticassertion 8y agoA common use case might be something along these lines: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7929c7ee98bf530888173d51a71f96a1 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.
- chrismorgan 8y agoIncidentally: for what that whole `match` block could be with the entry API: *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).
- Sharlin 8y agoIn general, Rust will probably never be the sort of Swiss Army chainsaw that C++ is, and that is by design. For instance, there will most likely never be custom move semantics; the fact that Rust moves are always simple bitwise copies is a feature. But we're probably talking about different types of expressiveness—what I had in mind was modern type system features such as algebraic data types and pattern matching, monadic/combinator-based vocabulary types such as Option, Result, and Iterator, trait-based polymorphism and so on. C++ is slowly getting many of these as well, but often the corresponding features feel clunky and "design-by-committee"-ish.
- zozbot123 8y ago> objects that might know about pointers to them 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.
- blub 8y agoThe terrible thing about the C++ and Rust solutions is that one needs to understand many idioms and quirky syntax choices in order to comprehend what's going on. 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.
- DougBTX 8y ago> and (1..) = c, whatever the heck that means 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.
- mclehman 8y agoThis exact thing is one feature I like about Perl 6 and (minorly) dislike about Rust. 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.
- mehrdadn 8y agoA downside of 1..10 is, how do you represent an empty range starting from 1? 1..0? That looks so weird... I'd expect it to mean a backward range starting from 1 and going up to 0 inclusive. A nice thing about end-exclusive notation is you can denote backward ranges, forward ranges, and empty ranges with the same notation.
- peheje 8y agoI'm not sure about the specific code sample, but take a look at Nim. It's python syntaxy but 'fast' and compiled.
- mehrdadn 8y agoThanks, I've heard good things about it! I tried for a bit but then had to do other stuff, but I'll finally have to dig into it at some point... just haven't managed to bring myself to make the full investment.
- philwelch 8y agoIf you want static typing and better performance than Python, there’s still Java and Go.