4 ms·
My general philosophy is to get my tools to work for me as much as possible (a big reason I love rust so much). Years of using C gave me a huge appreciation fo
by xedrac 4y ago
My general philosophy is to get my tools to work for me as much as possible (a big reason I love rust so much). Years of using C gave me a huge appreciation for RAII when I started using C++. To my horror, a lot of C++ codebases actively eschewed this very simple, yet liberating tool. There probably are some use cases where RAII isn't ideal, but I have yet to encounter one. And I'm not just talking about unique_ptr and shared_ptr. This is one of the biggest reasons I'm not excited about Zig. Defer is like a very poor man's RAII. Rust is simply next level - as big an improvement as RAII was over manual memory management.
- hansvm 4y agoClosures are poor man's objects and objects are poor man's closures. Same with RAII and defer. You can pretty easily map code using one to code using the other. Is init/deinit/defer that conceptually different from RAII?
- pjmlp 4y agoOnly in regards to enforcement, init/deinit/defer require help from static analysis to guarantee they aren't forgotten to be called for resources in block scope.
- stingraycharles 4y agoAs someone who has written a lot of C++ but also languages with other constructs that solve similar patterns (defer, finally, etc), I’d say that RAII is definitely the most flexible winner of all, but requires more code bloat. You’ll need to write a class with at least a constructor / destructor, give it a name, etc. “Just” a defer statement is absolutely more simple, and puts the resource reallocation logic right next to the allocation logic, which is a nice feature.
- bestouff 4y agoExcept in Rust you nearly never write a destructor. Unless you have very specific needs it's always generated automatically for you.
- zarzavat 4y agoYou can easily implement defer in terms of RAII and lambdas. RAII is a composable building block to build higher level patterns. The whole is greater than the sum of the parts.
- stingraycharles 4y agoThat’s the point I’m trying to make: RAII is more flexible but requires more code than a defer or finally statement.
- IshKebab 4y agoBarely any more. It can be as simple as auto sg = scope_guard([&]() { delete whatever; }); E.g. see this implementation: https://ricab.github.io/scope_guard/ https://ricab.github.io/scope_guard/ But also I should make the point that you generally don't need to write defer-like statements at all when using RAII because it's implemented in the actual type. So it's only more code overall for types that are used only once or maybe twice.
- metadat 4y agoDo you really consider this simple, or is it sarcasm? Simple compared to what?
- IshKebab 4y agoIt is simple. At least if you know C++. I can see why it might look like it has a load of random brackets if you aren't a C++ programmer. But anyway if you're doing C++ right you would rarely need to actually do that whereas you need `defer` in Go all the time.
- pjmlp 4y agoIf that is too much typing, a macro can help.
- pjmlp 4y agoThanks to C++ flexibility there are plenty of "defer" utility classes to chose from.
- klodolph 4y agoMy experience with defer is much more positive. I too, switched from C to C++. But defer works pretty well. I often end up writing small classes in C++ just to invoke some piece of code on clean up. Sometimes feels like a poor-man's defer, to me. RAII is the winner in terms of flexibility but in practice it seems like the benefits of RAII over defer are not so big.
- kllrnohj 4y agoThe benefit of RAII is that a return value can guarantee cleanup isn't forgotten about. You can't do that with defer. If I return an FD and I need you to close it, I can just enforce that trivially by returning an RAII type. The same thing in a defer language requires both reading function documentation and not forgetting the defer. Meanwhile, defer-like utility can trivially be created in top of RAII. So RAII is the more versatile and useful building block
- klodolph 4y agoI'm aware of the benefits of RAII. Those static analysis tools are really, really easy to use. For example, if I forget to invoke cancel() in context.WithCancel() in Go, it gets caught automatically. Something defer-like is annoying to create on top of RAII (or at least annoying in C++). What you put in defer can be a whole function, with access to the enclosing scope, and (critically) the return values in Go, and can know whether the program is panicking (equivalent to throwing an exception). In C++, variables in the enclosing scope are being destroyed, and since any defer implementation would be in a destructor, you can't throw an exception from the destructor. In Go, you can propagate errors from the code called by defer (use the deferred code to return an error result from the enclosing function), or panic() inside defer, and it's a normal error or normal panic. It's not something you could reimplement easily with destructors in C++. 99% of the time you're not using these fancy features. That's fine. 99% of the time, your RAII is just destroying an object at the end of scope.
- pjmlp 4y agoOne benefit is that with defer/using like statements you need help from static analysis not to forget to call them.