Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'm a strong believer that if C had defer, many bugs would disappear. The number 1 issue I find myself having when switching from C++ to C is missing RAII (Main C++ way of implementing defer).


> number 1 issue I find myself having when switching from C++ to C is missing RAII

That's because you've become complacent; you've gotten used to the false comfort of destructors. C++ destructors promise that you can ignore object destruction in business logic, and that's a false promise.

Assume you have a function that already has a correct traditional (cascading gotos, or arrow pattern) exit path / error path. Assume that you have the (awkward) defer-based implementation of the same function. Assume you need to insert a new construction step somewhere in the middle. In the defer-based implementation, you insert both the new action and its matching "defer" in the same spot.In the traditional implementation, you locate the existent construction steps between which you insert the new construction step; you locate the corresponding existent destruction steps (which are in reverse order), and insert the new destruction step between them. The thinking process is more or less the same, but the result differs: without defer, your resultant source code shows what will actually happen on the exit path, and in precisely what order, and you can read it.

I think defer is awful.


See but the issue is that humans are not perfect, and time is usually a resource.

Without defer like mechanisms objects get leaked, mutexes held after return, etc...

In a perfect world everything could be perfectly explicit as infinite time and energy has gone into ensuring nothing is forgotten and everything / every code path is well exercised.

Even then, scoped based resource acquisition / releasing still feels more ergonomic to me.


I agree about missing RAII when switching away from C++ but it seems like defer cleans up after enclosing function exits while RAII cleans up when object goes out of scope, which is more fine-grained. Maybe I'm misunderstanding exactly when defer would clean up but it seems more like a safety feature. As people pile more stuff into the function the cleanup would be deferred more and more while RAII encapsulates the management right where you need it, e.g. exiting a loop or something.


defer-based cleanup also has the problem that, if the function plans to return a value it has to make sure to *not* defer its cleanup, and the caller then has to remember to clean it up. Destructor-based cleanup avoids these problems, but of course it's reasonable for languages (existing ones like C and even newer ones like Zig) to not want destructors, so defer is the next best thing.

Note that C++ destructors are also not ideal because they run solely based on scope. Unless RVO happens, returning a value from a function involves returning a new value (created via copy ctor or move ctor) and the dtor still runs on the value in the function scope. If the new value was created via copy ctor, that means it unnecessarily had to create a copy and then destroy the original instead of just using the original. If the new value was created via move ctor, that means the type has to be designed in such a way that a "moved-out" value is still valid to run the dtor on. It works much better in Rust where moving is not only the default but also does not leave "moved-out" husks behind, so your type does not need to encode a "moved-out" state or implement a copy ctor if it doesn't want to, and the dtor will run the fewest number of times it needs to.


This defer (using attribute cleanup) as well as Zig's are run when going out of scope. Go's runs at function exit.


Yeah, defer within scope is the most ideal form in my opinion.


The major source of bugs in C is located on string.h, and nothing has been made in 50 years to fix that.

Really fix, not mitigations with their own gotchas.


There have been things done to try to fix that, see "strcpy_s" and other related functions. Visual Studio even considers use of the classic string functions (like "strcpy") to be a compiler error. You need to define a specific macro before you are allowed to use them.


The much dreaded Annex K functions are perhaps the worst possible example of an attempt at "fixing" anything safety related in C. A waste of ink.


You missed my second paragraph.


...and a we would get a host of new bugs that would be a lot harder to fix. Invisible jumps are very bad.


Nearly every modern language supports defer in some sense. Unless you are talking about RAII hiding the defer, but that's not the case with a custom Defer type or similar that takes a closure in the constructor.

Defer is a way better solution than having to cleanup in every single failure case.


> cleanup in every single failure case

you're only tempted to clean up [fully] in every single failure case if you don't know how to implement cascading gotos or the arrow pattern.


I'll be honest that you are in the minority here.

goto is widely considered an anti-pattern and so is the arrow pattern.


> you are in the minority here

Yes, I am.


> Invisible jumps are very bad

Agreed; they're terrible. Implicit sucks, explicit rules.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: