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

I have seen a claim that Ada now has or is getting something akin to C++'s destructors or Rust's Drop trait. What would Ada's version be called, and what would it look like?

To me, the destructor is the single most distinctive and powerful feature of C++, adopting it is what made Rust viable, and lacking it makes Zig overwhelmingly less interesting than it might have been. If Ada got destructors, that would promote the language, in my view, from dead to potentially viable.



Ada's version of this is called controlled types. A type can have a finalizer method that gets called when a value needs to be destroyed.


Is it possible to precisely control when it gets called?

If not, how predictable is it?


Yes, you have complete control over this. In short, it's a hybrid between manual and automatic memory management. Memory allocation is a complex subject in Ada, and I'm not familiar enough with it to explain all the nuances, but here [1] is a good discussion.

[1] https://stackoverflow.com/questions/67131931/does-ada-deallo...


Zig's thread on the topic is interesting reading - it has gone from open to closed several times, and includes interesting examples from the stdlib where resources were not closed properly: https://github.com/ziglang/zig/issues/782


Doesn't the Ada.Finalization package (since Ada 2005) give you this anyway?


No. To be useful it needs to be a core language feature.


Languages like Ada provide hooks, for the runtime and compiler to collaborate together, because safety is more relavant than performance at all expenses.

Any variable declared as controlled type will have their "destructor" called just like C++, and that is what matters.

Like like RAII doesn't do anything, if the type is heap allocated and due to a bug not deleted or freed(), and someone has to write the destructor code, just like someone has to write the controlled type.


> Languages like Ada provide hooks, for the runtime and compiler to collaborate together, because safety is more relavant than performance at all expenses.

Does Ada come with optional garbage collection, at least? Memory leaks are a big safety concern, perhaps the biggest in practical code, and IIRC Ada still doesn't have GC "by default" for some unimaginable reason.


Ada 83 had one, however production deployment scenarios never required one so Ada 2005 dropped it, and nowadays thanks to SPARK and controlled types is not required anyway.

Additionally Ada controls many types directly like strings and vectors, and storage pools.

I recommend the FOSDEM talk about Ada memory management.


The unimaginable reason is that one of Ada's core competencies is realtime embedded systems. Very often, these don't allocate or free memory ever.


Don’t a lot of those systems allocate and free slots in large arrays, which is almost as risky?


Ada arrays are actual types, they're bounds-checked, the language provides some niceties for indexing, and Ravenscar and SPARK are available. You're going to have less issues with Ada in that case than you are with just about any other language out there.


`Ada.Finalization` is a core language feature, it's a package defined as part of the standard. Like many Ada features, RAII (via Controlled Types) is something you can opt into, such as if you need more than just record initialization.


I am corrected.


It is a core language feature as of Ada95. Ada, unlike most other languages generates calls into the standard library to implement complex features, controlled types and tasks are two such features. And no, I don't mean generating simple calls to compiler intrinsics.


It is a core language feature. You don't understand what Ada packages are.


Core language feature and package are not necessarily the same thing. No reason to bump heads. Instead you could provide a link to the RM: http://www.ada-auth.org/standards/12rm/html/RM-7-6.html



What do you use destructors for?


Everything, really. They are the only bit of runtime automation ever invented, besides Garbage Collection, but manage any sort of resource, most trivially memory. Without, everything needs to be programmed by hand, everywhere, fallibly. With, libraries can do the work, invisibly and with absolute reliability.


Absolute reliability, well

    {
        raii_handle *my_type = new raii_handle(new type);
        ....
    } /// ooops
Naturally it doesn't happen just like that, rather in 500 lines of code function in code that has already been through 10 contracting agencies, including some offshored ones.


Oh, so all my smart pointers I have written in Ada don't finalise when Finalize is called, by the runtime?


The example is about C++.


He was complaining of no destructors in Ada, when there are.


While asserting the reliability of RAII in C++, which only works out if you don't have devs that do stupid stuff like the example above.

You might consider that example stupid and argue no one would ever write code like that.

To which I would answer you haven't reviewed enough offshored C++ projects.


Would maybe be nice if there was a static check in C++ to verify whether something was declared on the stack, then that could be used to eliminate some of the potential RAII mishaps.


Ignoring libraries does not demonstrate anything about their reliability.

  {
     auto p =
       std::make_unique<type>;
     ...
  } /// no oops.


  {
     auto p =
       new std::make_unique<type>;
     ...
  } /// oops again.
The fact being the "reliability" is a good as the hundreds of developers that touched the code.

Real reliabile features don't depend on their skills to actually work 100% of the time, and doing something like newing a RAII type would be a compiler error, but alas it isn't.


Anybody can write crap code in any language, Ada very much included. So, the example is meaningless BS.

What matters is whether it is easier to write crap that compiles than correct code, and whether the easiest sort of crap compiles. That is where there has been progress. If you never, ever need to write "new" anymore, why would you accidentally write it?

The BS example above does not, in fact, compile, because make_unique<> is not a type. So, it illustrates the progress I cite.


Yes the example is wrong, I was just trying to make a point while copy-pasting on the phone.

One thing Ada was designed for and C++ not, was to prevent crappy developers to mess up.

Anyone that has reviewed C++ code from offshoring Asian companies is well aware of what I mean.


Your point was wrong. Your example illustrated that your point was wrong.

Bad code from dodgy, offshore outsourcing services may be in any language. Bad Ada code is no better than bad code in any other language. Bad Ada code could be worse if it often works by accident, where other code might have failed visibly and been rejected.


> Bad Ada code is no better than bad code in any other language.

Ada's strictness allows it to catch many more errors at compile time than, say, C.

> Bad Ada code could be worse if it often works by accident, where other code might have failed visibly and been rejected.

This is backward. Errors will be more easily detected in Ada than in, say, C. C is full of footguns and undefined behaviour. Vanilla Ada still isn't actually a safe language, unfortunately, but as a matter of degree, it's much safer than C.


What about Python's "with" statement and context managers? Or Common Lisp's "with-" macros? Or Java's try-with-resources? I'm sure many other programming languages have automatic exception-safe resource management constructs as well.


"With" must appear in client code, much like "finalize" clauses in Java and in similar GC languages.

That is the opposite of automation: everywhere you need cleanup, you have to repeat the incantation, and get it right, again, every time. The point of destructors is that they are wholly contained in the library. It takes no extra client code to run them, and they are exercised identically on every single use of the type, so are well tested. Identically the same code runs on a throw, so there is much less risk from usually poorly exercised failure cases.


Freeing memory, releasing locks, closing files (though destructors are less expressive than separate close methods because destructors can't return error codes, which is why I want linear types which can make calling a close() method mandatory), committing transactions (though some argue it should be an explicit function call)...


In .NET land there are Roslyn analysers that trigger a compiler error if you don't call using on a IDisposable type.


Implementing RAII. It's the singlemost fundamental C++ idiom.


Indeed, and although already possible in the very first C++ compilers, it is still foreign enough that many include it as part of modern C++.


It is utterly foundational to modern C++. You could not leave it out and even make sense.


It foundational to C++, period. Even in Turbo C++ 1.0 for MS-DOS released in 1992 or thereabouts.

The fact that many still think it only came with C++11 or whatever "modern C++" is where the problem lies.


Even in Zortech C++'s 1987 release!


Thanks for the heads up.

Borland and Microsoft ruled the compilers party in the Iberian Penisula during those days. :)


It is the foundation of C++. Even in pre-modern destructors were the heart even if we didn't use them as much as we do now with modern C++.


We have always relied on destructors fully as much as we do now. What is new is that we hardly ever need to write one, anymore, or even to declare one. That is a consequence of a more mature standard library.

I wonder now whether Ada auto-generates destructors for types with a member that defines one. And whether its standard library is such as to make a need to code them yourself vanishingly rare.


Releasing allocated resources automatically, for instance. There are many possible applications.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

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

Search: