C++'s Rule of Zero
turingtester.wordpress.com
turingtester.wordpress.com
C++ has been trying to get single-ownership semantics to work right for almost two decades. "auto_ptr" went through three standards revisions before the C++ standards committee finally gave up and dropped it. Now there's unique_ptr, where, after sprinkling on some move semantics, almost works, with fewer holes than auto_ptr.
Getting this right needs a borrow checker, like Rust's. Now that borrow checking has finally been figured out, there's a way out of this mess. Trying to do borrow checking via the type system just doesn't work out well.
Apple is now going full speed with Swift. All WWDC talks that weren't Objective-C specific used Swift.
Microsoft is happy going .NET Native + C++/CX.
UNIX tends to be married with C, due to the way they were developed together.
I doubt Firefox OS will ever allow for Rust based applications.
MirageOS is probably the one in the best position, but it will stay a research OS.
GenodeOS and L4 are also full C++.
Embedded OS vendors are pretty much a sample of real time Java, C and Ada.
So from the perspective of real systems programming I see an uphill battle for Rust.
But yeah, there is a real need for safer systems programming languages.
Sure it will. Compile them to Web Assembly first :)
People have already run Rust code through Emscripten. (It's not well supported at the moment, but the amount of work to be done to polish it up isn't large.)
See what happening?
All Rust needs is LLVM support and a stable (probably C) ABI for OS features that it can link against (like libc).
Until then it is just yet another programming language with an AOT compiler, whwre there is already a plethora to chose from.
We should learn from history of systems programming languages.
None of them survived without being tied to a specific OS.
Well, the borrow checker and safe manual memory management are unique features that allow moving a lot of code that previously had to be C++ for performance reasons to a safe language. (I've elaborated why safe dialects of Ada and such are not the same in previous posts.) I wouldn't characterize Rust as "just yet another systems programming language".
I have meant if Rust fails to get adoption as systems programming language, it will become just another business language.
Another interesting route would be kernel modules- Rust could be a good language to write drivers in, if you need the extra security.
I don't think it necessarily needs a whole OS to itself just to survive.
How do you define systems programming? If you define it as writing operating systems then your statement is a bit of a tautology. If you define it as anything other than applications programming then I would say there are successful languages not tied to a specific OS, first and foremost Java.
Systems programming languages are characterized by requiring minimal run-time support, or supporting a version of themselves which is that way (e.g. ISO C has the concept of a "freestanding implementation", and there is a way for such a thing to be conforming).
As a rule of thumb, if it has no "freestanding implementation" or equivalent, then it's not a systems language.
I'm not aware that a flavor of Java exists with a toolchain that would let us rewrite U-Boot in Java, while keeping its footprint in the same ballpark. (I.e. the firmware image cannot contain an entire Java platform.)
I'm _trying_ to learn Rust at the moment, but parts of the official book, as well as parts of Rust's standard library (function naming) have been horrible to read up to now.
Good points though - there are huge ecosystems out there in the games / graphics / VFX industries of C++ code and libraries, and I doubt any of it's going away any time soon.
All those thousands of lines of C++ code, you can easily use them in any language you choose, except for Java.
- copy by value for small datums
- store large datums in shared-ptrs if pointer access speed will not cause a bottleneck
- Relational model: I use large database objects for storing inter referencing objects - i.e. I don't use any pointer type for storing refences in large interlinked structures but handles such as indices or map keys.
I agree, trying to implement borrow checking or garbage collection 'quickly' through the type system in C++ usually leads to a world of pain.
Better designed languages offer poor intuition to C++ in this sense because the examples in those languages leverage the well designed type system. If one wants to do advanced stuff in C++ the correct way is to model that in the program code and not through clever constructor tweaks.
I'm not sure this a good chapter in that dialog. There are simpler (simpler in the sense 'less complected' and not easier) ways to handle resource management in C++ than this default ballet.
The example is clever C++ code. Most of the time clever C++ is bad because the stupid verbose way would be easier to understand and maintain. So, before demonstrating some intricate template scheme or inheritance trick there should be a good reason presented why it makes things simpler.
I agree on what you said but this example is a poor device if we are discussing code in a production setting.
Local maxima are a bitch.
If you're claiming that the verbose way is just writing out the special functions, then I can assure you that it is not easier to maintain. If you are saying that the class in question could simply write a nested class that wrapped unique_ptr and had the correct copying behavior, maybe, there are arguments for both.
This example is fully intended to be solid code for production use, not primarily to be "clever".
I have nothing against the pure technical aspects of your article.
I originally answered to the poster who said this was a good example why Rust was better and my analysis was written from that point of view.
To me this was a poor comparison in the general sense since one can write production C++ using just copy-by-value, RAII semantics and stl containers and handling exceptions by letting the program terminate.
"If you're claiming that the verbose way is just writing out the special functions, then I can assure you that it is not easier to maintain."
I suppose I found this example lacking in the comparison-to-rust sense precisely because it invented this complected system that needed patching over. While real life systems often evolve through neglect and haste into piles of spaghetti this cannot be used as a direct evidence against a specific language in particular since it is more of a psychological and organizational rather than a language problem.
Rust hasn't seen enough widespread use to find all the problems and limitations with the language. The borrow checker seems great, sure. But looking at how Rust handles moves vs copies, I'm not convinced at all that it will be good enough in the long run.
This "mess" is caused by imbuing Example with correct move and copy semantics. In rust, it seems like most types have one or the other, but not both. If a type does not implement Copy, then when you try to copy it, it automatically gets moved from. So u = v would change the value of v. Ironically, that's similar to the behavior of auto_ptr that you just criticized, and quite unintuitive.
I just think it's a bit amusing, people complain about this sort of thing in C++ all the time. I can imagine if Rust takes over people will complain how you have no idea what u = v is doing unless you know what u and v are.
I'll admit my biases, but I prefer the c++ way: types know how to both move and copy themselves, and they will copy by default, but move when it's safe (rvalue) or explicitly asked to (std::move). I like looking at auto u = v and knowing that u is always a copy of v.
The `Copy` trait (types that don't move) is just for types where memcpy is a valid & safe way to duplicate values of those types, it's not at all like C++'s custom copy constructors.
See also my answer here: http://stackoverflow.com/a/24253573/1256624
It's time to move some C++ development to something. But there are too many choices to be able to just flatly declare that something to be Rust.
A lot of what is done in C++ should move to an actual high level language.
The talks from CPPCon 2014, made me realise there are still lots of devs using it as C with a C++ compiler.
I realised the lack of C++ knowledge when he said "what is push_back???" with a vector. I can only imagine the brain implosions when lambdas replace all those stupid struct unary predicates I have to litter my code with, and when I implement move operators.
I am trying to convert my code to C++11 (mostly it is 03 style) which will necessitate a move in Windows compiler version but no big problem (and not too many changes - I use STL containers everywhere).
Really? Rust has problems of its own. For example, I don't like that it moves (on assign or parameter pass) by default (versus copying in C++). My feeling that you'd end up explicitly cloning a lot. Then, there are no exceptions, and I'm not going back to dealing with error codes. Also, code in Rust looks a lot noisier than in C++ due to an excessive use of sigils in the language.
I was initially interested in Rust, but my interest faded when I read a bunch of (official or semi-official) tutorial all of which seemed to focus on just one thing: types of pointers in Rust (how many? 5?) and borrowing semantics. Having not experienced any significant problems with ownership and resource management in C++, Rust didn't look like a worthy investment of time.
You still may not like Rust anyway, but if you haven't given the language a fresh look since well before 1.0, a lot has changed, as your opinion may have too.
Relax. Give it some time. Unless if you've written a gigantic widely-used project in Rust, you probably shouldn't be telling other people to go do that at this point.
The rule might be expressed as "try to write classes that are adequately served by the compiler-generated special functions" (which the compiler will write consistently).
If all your member objects have destructors, there should be nothing left to do. The generated destructor recurses over these and that is that. So writing a custom destructor is for cases when something additional needs to be done because some members don't clean themselves up, or there is some other issue even if they all do. The Rule of Zero seems to be saying that these cases tend to be code smell.
Anyway, of course you have to write (or at least declare) a destructor if you need a virtual one, at the bottom of a class hierarchy.
~MyAbstractBase() = 0;
The language-supplied destructor will not be virtual, and certainly not pure virtual. virtual ~AbstractBaseClass() = 0;
...
AbstractBaseClass::~AbstractBaseClass() { } class A {
public:
virtual void foo() = 0;
};
void A::foo() {}
class B: public class A {};
B b;
b.foo();
and that will work?There was an old Herb Sutter GotW about this: http://www.gotw.ca/gotw/031.htm
All C++ classes are dedicated to managing the memory resource that they are allocated on, am i missing something?
I mean it's fine if you are competing with Python or something ,but if you actually care about perf this is never going to work. But of course anyone wanting to coin a "Rule of X" doesn't want to see a problem in its full context, they just want to be able to pretend to have a solution well enough that they can at least fool themselves. Etc, etc.