9 ms·
I'm bored of all the Rust vs. C++ comparisons, when the C++ code is something you simply don't write with modern and idiomatic C++11 and C++14. Not that you cou
by danieljh 12y ago
I'm bored of all the Rust vs. C++ comparisons, when the C++ code is something you simply don't write with modern and idiomatic C++11 and C++14. Not that you could not write it, but seeing this would not pass me reviewing the code.
The add_one example, as simple as it is, is a horrible example.
Why not embrace value semantics and write it like:
int add_one(int x) { return x + 1; }
There is simply no need for pointers or references. Write simple code first. To the heap allocation example:
auto x = std::make_unique<int>(5);
Even if this is a silly example, just say no to unnecessary heap allocation. Why not:
int x{5};
Now to the lifted example:
void add_one(std::unique_ptr<int> num) {
* num += 1;
}
This mixes operations with lifetime management. The add_one function does not need to care about ownership. And it does not need to take ownership of its argument. Instead on the call site do:
int add_one(int); // from above
auto p = make_unique(5);
auto six = add_one(*p); // <-
There are valid points in the blog post and I know that most snippets are meant as examples. But as someone writing C++14 you can't win me with those kinds of posts, even though I'm having a fun time playing with Rust in my spare time.
For more about modern practices see: http://klmr.me/slides/modern-cpp/ http://klmr.me/slides/modern-cpp/
Also see Howard Hinnant's unique_ptr tutorial: http://howardhinnant.github.io/unique_ptr03.html http://howardhinnant.github.io/unique_ptr03.html
- tekacs 12y agoYou seem to be pretty fundamentally missing the point of using those examples. All of your examples look almost identical in Rust and C++. That's not a positive, nor is it a demonstration of similarity - it's a useless set of examples for a post _demonstrating the differences_ between the two. :P It's worth looking at each of the examples in the article and thinking about them in the context of the more complex practical problem that do _require_ mutable/heap state. Again, think of the examples as 'C-Reduce'd[1] ones - just enough for an experienced programmer to 'get' the differences and infer its relevance to real world cases. To give at least one motivating example: for your first case (functional add one) - that's entirely passable for the (tiny) int, but quite useless for the larger case of BigInteger. That's where Rust's default move semantics would really shine, but using BigInteger as an example would introduce lots of irrelevant semantics, making the example bigger but scarcely more informative. [1]: http://embed.cs.utah.edu/creduce/ http://embed.cs.utah.edu/creduce/
- danieljh 12y agoI'm not missing the point of those examples. They showcase Rust vs. ridiculous C++. See my point with making the add_one function not taking a unique_ptr. This is valid criticism and valid in more complex practical examples, too.
- tekacs 12y agoBigInteger& add_one(BigInteger& num) is an awful function (really no checks of any kind, such as multiple holders). You suggest that it's valid for more complex practical examples, but that's a simple example where you'd probably want: void add_one(std::unique_ptr<BigInteger> num) BigInteger is a common example often written in fluent/mutable style or to take ownership of the argument in just this way.
- danieljh 12y agoYour second function still mixes ownership with resource management. Taking an unique_ptr<BigInteger> by value means, add_one takes the ownership of it. But it does not return anything, making it a useless function. The though-process goes like this: * Do you need read-only access to the argument, take a const T& * Do you need to modify the argument, take a T (by value), letting the call site decide to either pass an rvalue (move it, no copy), or an lvalue (copy) There simply is no need for unique_ptr<BigInteger>, as BigInteger already handles its resources internally (with move semantics). It's the same reason as to why an owning pointer to a vector is silly when a vector already handles ownership of its resources. http://klmr.me/slides/modern-cpp/#9 http://klmr.me/slides/modern-cpp/#9 is exactly about this.
- tekacs 12y agoYup, I agree with you (and pjmlp, who I can't reply to) on all of this. I can see where you're coming from in pushing for all-value semantics, taking advantage of the mechanisms built into the language to control how those semantics play out. I think it's worth remembering through all of this that getting just the right semantics requires a little more effort and thought on the part of the callee, in C++-land, as well as which the relative lack of consistency (and opaqueness) in these semantics, at least in the absence of a full IDE or similar to jump to signatures. Oh and briefly, the C++ may be unidiomatic, but it's perhaps useful as a C++ mimicry of the Rust code to show maximally similar semantics? I think perhaps the dismissive nature of your GP post blinded me to exactly what standard you wanted the C++ to hold to (oh and also, swapping out heap allocation still seems to be missing the point, as there are definitely cases where that's the 'right' behaviour)
- Inufu 12y agoI think you missed the point. Of course you'd never implement add_one that way, but at some point you'll have a function where you want to pass a unique_ptr as an argument, and then your code will happily continue to compile even if you use the pointer after the move.
- danieljh 12y agoAccessing a moved-from object is one of the examples I didn't criticize because it could be an issue and I hope for compilers warning about it. That doesn't make my other claims invalid, though.
- sanderjd 12y agoThere is always a tension between making examples easily comprehensible and making them realistic. People often use references and pointers to integers as examples because they have (arguably?) the lowest conceptual overhead to distract from the point of the article. So I think you make good points, but that a more charitable reading of the examples would substitute "some type I would realistically put on the heap and make references to" everywhere you see "int". But in general, I think some of the advantages of Rust boil down to "compiler enforcement of stuff that good practitioners of modern C++ already do". And that's a good thing. Thanks for your links, which are great!
- steego 12y agoI think you make an excellent point that this is a poor demonstration if one is trying to convince a decent C++ programmer to try Rust. Many of these issues are simply not a problem for moderately disciplined C++ programmers. If anything, this sort of post is more convincing to a junior developer who wants their compilers to do more to warn them when they've done something stupid. They want to write idiomatic C++, but they're lazy and inexperienced, so if there's a language where the compiler provides feedback when they deviate from what's idiomatic, they're going to opt for the feedback. I'd be very interested in what's drawn you into trying Rust and what you think are the most redeemable qualities.
- danieljh 12y agoI'm trying Rust for the same reason why I'm investing weeks into learning Clojure. Because I'm convinced learning more approaches to high-level problems (like ownership, concurrency, typing, patterns) is one way to improve my overall skill set and thinking. One thing I like about Rust is how you e.g. can specify a Sortable trait and then with T: Sortable require it for types. This will eventually come to C++ with Concepts (lite), for now there are ways to work around this, e.g. with: #define requires(...) typename enable_if<(__VA_ARGS__), int>::type = 0 template <typename T, requires(is_integral<T>())> void fn(T); Without a feature like this you may see cryptic errors from inside template instantiations coming from your stdlib, when you try to sort something that is not sortable.
- CountSessine 12y agoI agree completely. None of these examples are going to convince a C++ programmer who's already sunken the cost of learning the language to switch to rust. Something I realized at some point is that these "language A vs language B" comparisons aren't really there to convince adherents of language B to switch to language A. You'll see lots of comparisons on the web of Haskell/Scheme/Lisp/C#/F#/Ocaml etc put up against really poorly written C/C++ code, with "language A" inevitably coming out of the comparison pretty well. Thinking back to my days at school, a lot of physicists were telling each other that if they wanted their simulations to run in a reasonable amount of time, they had to code them in C. These comparisons are really aimed at them - the message here is, "yes, a really great C programmer could write machine-optimal FFTs or whatever in C, but the code that you, a beginner, will end up writing could actually end up being not much faster and in some cases perhaps even a lot slower than the equivalent code in Haskell/etc".
- pjmlp 12y ago> I'm bored of all the Rust vs. C++ comparisons, when the C++ code is something you simply don't write with modern and idiomatic C++11 and C++14. Not that you could not write it, but seeing this would not pass me reviewing the code. Seeing last year's CPPCon videos was illuminating to me. A big problem is that many are still writing pre-C++98 style or chained to style guides that prevent many of the C++11/C++14 improvements. Specially problematic when IT is the one calling the shots to what is available in the development environments.
- santaclaus 12y agoIf IT is calling the shots you probably aren't going to have first class Rust support, either...
- pjmlp 12y agoAgreed.
- frankmcsherry 12y agoYes, you can also write safe code in an unsafe language, but until you are literally willing to code review every program anyone writes in your unsafe language of choice, there is value in having safe languages where we don't have to rely on your throughput and accuracy. There is a real difference between a safe language, and a safe subset of an unsafe language that gets verified by some guy (no disrespect). The former is (everything else being equal) just way better for everyone involved, especially for the guy.
- jfager 12y agoI'm bored of C++ apologists pretending like there's really such a thing as 'modern and idiomatic C++11/14' that any two C++ programmers actually agree on, or that you'd ever actually see practiced consistently in the wild. You can no-true-Scotsman code that compiles, runs, and blows up in your face with any modern C++ compiler all you want, it doesn't change the fact that Rust eliminates entire classes of errors that are trivial to hit in C++.
- pjmlp 12y agoI am looking forward to the day Rust is available out of the box in XCode, Visual Studio, Android, QtCreator. Until then, it is going to be "Modern" C++ for the lower layers/common code, with the platform vendor languages for the upper layers, regardless how Rust improves over C++. I am a big fan of the type safety of Pascal and ML language families, but eco-systems have more weight than just the language.
- trhway 12y ago> Not that you could not write it, but seeing this would not pass me reviewing the code. hear, hear... Few of my recent features were passing through 7 layers owned by 4 teams (C++) with one team also having Java subteam (who seem to have never heard about Java coding style, etc...) Each team has a person who like you is enforcing his the _only_ right way ... which happen to be different from each other. Well, i just code to whatever style that module is written in :) After 25 years in the industry i've seen a lot of "right ways"... >The add_one example, as simple as it is, is a horrible example. >Why not embrace value semantics and write it like: int add_one(int x) { return x + 1; } >There is simply no need for pointers or references. Write simple code first. yea, man. That particular style is there too. Slow as hell. Works for the teams of really average programmers who can't grasp and keep together in mind more than one simple aspect of a small piece of functionality at a time.