5 ms·
I think GP means RAII, which is still an open area of discussion in Zig: https://github.com/ziglang/zig/issues/782 https://github.com/ziglang/zig/issues/782 I'
by slimsag 5y ago
I think GP means RAII, which is still an open area of discussion in Zig: https://github.com/ziglang/zig/issues/782 https://github.com/ziglang/zig/issues/782
I'd love to see it if it can be implemented in a way that fits the language, but that remains to be seen.
- ncmncm 5y agoActually, no, I mean destructors. Equivalent to Rust's Drop trait. Those are needed for RAII, but also for about everything else. (You need other things, too, to make RAII.) They were the only thing that made C++ uniquely powerful in 1985, and the only reason Rust can begin to compete. They represent the first and still only piece of runtime automation seen anywhere in the programming world. (Some would claim GC, but it doesn't do anything.) Destructors are necessary for correct-by-construction coding. The only code you can be completely certain is correct is the code you didn't have to write. None of dnautics's questions ever arise. I don't have to think about them, and I can't get them wrong. If you imagine destructors have something to do with memory management, you have much yet to learn. Zig is doing almost everything right to become essential infrastructure, but will need to add some equivalent of destructors to get there.
- diragon 5y ago>The only code you can be completely certain is correct is the code you didn't have to write. This reminds me of Douglas Adams. "The major difference between a thing that might go wrong and a thing that cannot possibly go wrong is that when a thing that cannot possibly go wrong goes wrong it usually turns out to be impossible to get at and repair." This seems to be a matter of taste with no absolute conclusion to be reached which style is better. At least until we get significantly better at analysing codebases and software quality.
- papaf 5y agoI don't have to think about them, and I can't get them wrong. ... until you have to write a class yourself. Then you have to think of the complexities introduced by destructors such as copy semantics, move semantics and the destructors themselves. You also have to deal with architectural questions such as should my file have a close method (C++) or do I always leave this to the destructor (Rust). If I was to give an opinionated summary, a little complexity at the call site has been moved to a lot of complexity in the class implementation. Since there is, on average, one class implementation to many object uses, destructors might be considered a win. But I don't feel so strongly about it that I wouldn't consider a language for not having destructors.
- ncmncm 5y agoYou almost got there. What makes a language powerful is the ability to put essential semantics into a library where they can be got right once, so all users of the library benefit. It is worth design, implementation, testing, optimization effort if the results can be delivered over and over, in many different contexts, reliably. Destructors are the necessary ingredient to pull transaction semantics into libraries. Without, you are left to write comments explaining users' responsibilities, and users have to read and act on them perfectly, or be wrong. With, you can code them correctly once and users have no opportunity or temptation to get them wrong.
- papaf 5y agoI agree with your comment about libraries. Destructors biggest advantage is in well used libraries. Any interesting code involves custom classes written by less experienced developers that are only used once or twice in an application -- it is there that that the complexity of destructors hurt.
- ncmncm 5y agoThere certainly are design complexities in C++, around copy and move constructors and various operators. Some of those could have been avoided, in hindsight, and new languages can. None of those complexities spill over onto destructors. They were perfect in their original conception, and are unchanged 40 years on. They evoke no regrets. No bugs trace to anything unfortunate about destructors.
- masklinn 5y ago> ... until you have to write a class yourself. Then you have to think of the complexities introduced by destructors such as copy semantics, move semantics and the destructors themselves. That is a specific language issue. Rust doesn't share most of these concerns for instance, the only complexity is writing the Drop itself (which I agree can be subtle), but a type can't even be both Copy and Drop (and Rust has none of the Rule of X complexity of C++). > You also have to deal with architectural questions such as should my file have a close method (C++) or do I always leave this to the destructor (Rust). That seems less like an architectural question and more like a stylistic question. > If I was to give an opinionated summary, a little complexity at the call site has been moved to a lot of complexity in the class implementation. Except that's not actually true, much of the complexity you'll find in a destructor (excluding the complexities inherent and exclusive to C++ which nobody else is under any requirement to reproduce) will have to be implemented as whatever "cleanup" method is deferred. And then you need the defer system, and you need to actually call it which you can forget. So while destructors are definitely not trivial to add to the language, defer spreads the responsibilities and complexity arounds, and increases the occurrences of possible errors, as well as the number of failure modes. It also adds semantics ambiguities / complexities to the language: when you defer an expression, what is evaluated when? Dtors don't have that issue, you construct something according to normal language rules, then the dtor will be called at destruction.
- dnautics 5y ago> The only code you can be completely certain is correct is the code you didn't have to write. Not true at all, there are always compiler errors, and in many cases you can statically check your code using an external validation system. I'd be 99.999% confident of most of the WRITTEN code of SEL-4, probably more confident than ANY code "not written" in rust, because it's been formally verified by a theorem-proving external system that enforces far more invariants that the rust compiler. To be dramatic, the rust compiler after all will not throw a fit if you write fn add(a: i32, b: i32) -> i32 { a - b; } but if that kind of a mistake happened in the SEL-4 codebase you bet your butt the theorem prover would be unhappy. Correctness also can be other things that you might not be able to lean on the compiler to check so easily. For example, cryptographic side channels (timing e.g.). If there is ANY ambiguity caused by the inexplicitness of the code you produce, maybe because you're not thinking about the cost of the destruction event, because, after all, you didn't write it, and there's not lexical artifact left over, it can get extremely challenging to audit for those sorts of things.
- ncmncm 5y agoAnybody who cannot tell whether a type has a destructor is qualified neither to code nor to audit cryptographic primitives. In any case, cryptographic timing behavior is verified by careful white-box testing, not just inspection. Any bonus destructor activity (if, e.g., somebody unqualified coded it) would show up then. When your case is so weak, it is better not to present it.
- jorangreef 5y ago"doing almost everything right to become essential infrastructure, but will need to add some equivalent of destructors to get there." To be fair, if we come back to this opening argument, I'm still wondering how strong the premise here is? Are destructors in fact essential to essential infrastructure as you claim in your view? I'm not sure that I'm convinced by the case as you've presented it so far. I don't think we need to look too far to find counter-examples of essential infrastructure with the highest levels of safety that achieve this safety without destructors, and where destructors would be precluded by the safety model. Also, as someone who's done a fair amount of security work, the timing safe argument against hidden control flow is at least compelling and something to explore and not ignore out of hand. If a language design can make crypto simpler and safer to implement correctly, then I would say that's already a good indication of what the language may be able to do for less complex domains.
- bvrmn 5y ago> If you imagine destructors have something to do with memory management, you have much yet to learn. It is very limiting to think that destructors is the only way to solve resource scoping problem.
- masklinn 5y agoWhat alternative doesn’t add failure opportunities and require additional work by the developer where they will crop up? Both defer (and more generally scope guards) and context managers certainly do, to say nothing of try/finally.
- pjmlp 5y agoTrue, but also does RAII for heap allocated objects unless one makes use of smart pointers. Outside HN, CppCon and C++Now bubbles not all C++ codebases are as clean as we would wish them to be.
- masklinn 5y agoI don't understand the objection (?), I think there's at least one word missing, possibly more. If you want to use RAII for heap allocations you obviously need some sort of RAII wrapper for them. And smart pointers are the usual solution. But technically you can do without if you really really don't want to make dereference overridable, you can just have the "smart" bit (RAII / automatic deterministic deallocation) without the "pointer" bit.
- pjmlp 5y agoWhat your obviously remark misses out is my second sentence "Outside HN, CppCon and C++Now bubbles not all C++ codebases are as clean as we would wish them to be.". I have seen plenty of new std::string("some") and other similar crimes in offshored code, or code without ownership that gets updated by whatever external contractor got brought in to implement 5 Jira tickets that finally got some budget assigned to them. To be fair this isn't exclusive of C++, I have seen similar bad practices in other languages, C++ just gives more opportunities for them.