If Ada is already *very* safe, why Rust?

Examples of common usage of compiler pragmas would be to disable certain features, such as run-time type checking or array subscript boundary checking, or to instruct the compiler to insert object code in lieu of a function call (as C/C++ does with inline functions).

A lot of Ada advocates believe that Ada provides memory safety if you do not use pointers. This has also found its way into many descriptions of Ada.

But it is simply not true. Here is one counter-example that only uses core language features:

I’ve written a fairly literal unsafe Rust translation, too.


The article is interesting, but I am genuinely concerned by the widespread belief: where have you encountered that? That the so aptly named unsafe Rust is able to be unsound is not new :smile: and I expect people to be aware of that :grimacing:

By the way, I have taken the liberty to clean up your example a bit (no need for Uncopyable, and using ptr::read requires to mem::forget elsewhere, which your code did not (so your code actually had another soundness issue)):

/// To show unsoundness we implement type transmutation
unsafe fn magic<'a, A : 'a, B : 'a> (a: &'a A) -> &'a B
    enum Magic<'a, A : 'a, B : 'a> {
        A(Option<&'a A>),
        B(Option<&'a B>),

    let mut magic = Magic::B(None);
    let b_ptr = match magic {
        | Magic::B(ref at_b) => at_b as *const Option<&B>,
        | _ => unreachable!(),
    #[allow(unused_assignments)] {
        magic = Magic::A(Some(a));
    (*b_ptr).unwrap() // <- unsafe-ly dereference the invalid b_ptr

Now, you say that no dangling pointers are involved, but I think that it depends on the definition of dangling pointer. Sure, the pointee *b_ptr has not been freed, but since it has mutated / changed in shape since the moment the reference/pointer was created that could count as dangling.

But in any case I think we are both saying the same thing: lifetimes are paramount for soundness :slightly_smiling_face:

1 Like

I am confused as to why this can’t just be done with some pointer casts, like so

unsafe fn magic<A, B>(a: &A) -> &B
    &*(a as *const A as *const B)


unsafe fn magic<A, B>(a: A) -> B
    let b = std::ptr::read(&a as *const A as *const B);

The idea was to avoid casts / transmutes I think: “enums as safe? unions”

1 Like

Is it not true though, that SPARK doesn’t have this hole?
For instance, since you cannot have functions with side-effects in SPARK, your example would not pass the SPARK validation (examiner).

Since nobody else responded, I might as well.

struct Foo(u32);
let foo_box = Box::new(Foo(42u32));


  1. Declares a single-element data structure named Foo (struct Foo) containing a 32-bit unsigned integer which, being the first/only element in a “tuple struct” (no explicit field names), will be given the field name 0. (In Rust, such single-element structs are commonly used to define new types that have the same in-memory representation but will not be treated as equivalent by the compiler and will have their own different set of available methods.)
  2. Creates a heap-allocated (Box::new) instance of it containing the number 42 (42u32 is an integer literal that’s being explicitly declared as a u32 rather than type-inferred.) and stores the owning reference to it in the variable foo_box.
  3. When foo_box goes out of scope, the memory will be freed.

Thank you for the explanation.

SPARK is a fairly limited subset of Ada. I don’t think it is fair to point to SPARK each time an issue in the Ada type system comes up.

The type safety hole exploited by my example does not exist in SPARK because it does not have records with discriminants at all (whether with defaults or without defaults), at least in the version I’m somewhat familiar with. I’m not sure if the use of a function with a side effect is really necessary.

True, but limitations are being relaxed/superseded as the techniques to verify them are being discovered/implemented and perfected. For example, type invariants were added to SPARK in the 2017 release.

There are indeed flaws in Ada and it’s type-system — nobody’s saying it’s absolutely perfect, but it really is pretty good, certainly better than the ones like C or PHP.

Having extensively used both Rust and Ada, I find it difficult to see them as overlapping or fighting for the same niche. The two languages/communities put focus on very different things:

  • Rust gives a lot of flexibility to library writers, Ada is a much more opinionated language that puts a high focus on first-class language support for desired code patterns. Many Ada language features would only exist as libraries in other languages, but are not even implementable as a library in Ada (especially in the concurrency area).
  • Rust tries hard to be easy to interface to C/++ or bare metal, even if that constrains its type system. Ada has a lot more features that are extremely comfortable to use but don’t work well in FFI or without a runtime.
  • Rust has an unhealthy obsession with pointer types, Ada has an unhealthy obsession with arithmetic types (and to a much lesser extent arrays).
  • Rust’s main open-source compiler receives feature updates every six weeks, Ada’s main open-source compiler is updated once a year, mostly for bugfixing (sadly, language complexity makes compilers harder to implement…).
  • Because of this, Rust makes conservative people anxious, whereas Ada takes a lot of effort to appease them with plenty of certification and paid support contracts.
  • Everyday Rust features are easy to keep in one’s head after a few months of use (though advanced features will obviously take more time), whereas Ada has many corner cases that require keeping the RM close by even after a few years of use.
  • Rust can be hard to read/understand because it’s too terse/magical and the logic is invisible, whereas Ada can be hard to read/understand because it’s too verbose and the logic is drowned in noise.

Thankfully, there are also commonalities between the two languages:

  • Both languages make polymorphism a lot more explicit than is the norm elsewhere.
  • Both languages encourage error avoidance through very strong typing.
  • Both languages use strongly typed contracts for generics.
  • Both language communities strongly dislike undefined behavior.
  • Both language communities have a serious hubris problem when it comes to comparing themselves with the C/++ world.

If you will tolerate a lame metaphor, Rust feels like a box of woodworking tools while Ada feels like a CNC machine. The latter is a lot more sophisticated than the former, and does a lot more out of the box, but is also more specialized towards a specific kind of work. It’s unbelievably effective for its intended purpose, but feels more awkward when used for other purposes.


An excellent post (in the “so insightful that I couldn’t find much to critique” sense), but you made a typo which really grabbed my attention.

The first time I read that, I didn’t recognize it as a typo of “anxious” initially but, instead, spent about ten seconds wondering “What does axiom mean as an -ous adjective? Is it in the dictionary or a word cooked up on the spot?”

EDIT (which I posted at the same time as your reply):
That said, the one thing I did find to critique is that, for someone who’s used hand tools but never used CNC machines, your “toolbox vs. CNC” analogy takes more thought than it probably should because the first impulse is to see the CNC machine as superior in every way.


Thanks, fixed it :wink:

EDIT: Perhaps a more specialized tool like a drill or a hammer would have made the metaphor easier to understand, but then I would have been afraid of offending the Ada users. Ada is a relatively more specialized langugage than Rust, but it’s not that specialized on an absolute level, you can still build quite a lot of stuff with it. Well, as I said, it’s just a lame metaphor/analogy :sweat_smile:

1 Like

As a complete outsider to Ada, what do you mean with “obsession with arithmetic types”?


Like most other programming languages that I know of, Rust provides a simple abstraction of common machine types (via [i|u][8|16|32|64|size] and f[32|64]) and stops there. Ada takes it much further.

First of all, you are encouraged not to think in terms of what the machine can do, but in terms of what you want to do. This means that you can create integer types defined by their range of accessible values, floating-point types defined by their number of significant digits… and the compiler will pick the most suitable machine type for you given these constraints. In general, the language makes a large effort to significantly decouple the specification of arithmetic types (what you want from them) and their representation (how they’re mapped to hardware).

One “problem” is that in many cases, the compiler-selected machine type will do too much and allows incorrect values. Since the Ada designers are very serious about following specifications in a portable manner, there will be attempts to detect and report such events via compile-time and run-time checks, which is one thing to be mindful of when optimizing the performance of Ada code.

But let’s talk about a big positive consequence too. Bitfields and exotic integer types (such as those 10-bit ints from DSPs) are much more naturally expressible in Ada than in other languages, because you can much more naturally manipulate, say, 48-bit integers throughout your entire codebase, and not just fail at the point of inserting things in your bitfield where you realize that your 64-bit machine integer does not really fit in 48-bits. This flexibility in representing exotic integer types is, I believe, one reason why Ada remains popular in embedded developments to this day.

A natural consequence of this specification-based reasoning is also that everyone is going to want to define their own integer/float type, so Ada has first-class support for defining multiple incompatible integer and floating-point types, which can only interact with each other through explicit conversion. As you may imagine, this is super-convenient when you want to implement things like units of measurement (the good old Miles vs Meters types), but can also complicate interaction between libraries written by two different groups of people.

Ada also has first-class support for fixed-point arithmetic, which means using integers to represent fractional quantities (e.g. one possible mapping would be that 0.01 is represented as the machine integer 1, -4.2 is represented as the machine integer -420, and so on), and for all intents and purposes these look like floats in Ada code. I can only imagine that this is super-convenient when targeting those hardware architectures that do not have an FPU, although I never programmed those myself.

These are the main ones that I can remember off the top of my head.


Please, stop attaching new posts to this topic. The title is rude, and it hurts me when I see it on forum main. Think about a article named “If Star Trek is already very fun, why Star Wars?”, posted on the star wars forum. The answer is obvious though details may vary. I see recent posts in this thread are much informative and worth reading, but the title is rude enough to defeat all goodnesses.

Mmm, I tend to have a taste for troll questions myself, but personally that thread title didn’t offend that much. I read it as “We have too many programming languages (true), and it seems there were other languages targeting similar goals as Rust before (also true), how much did you study this state of the art and why did you choose to deviate from it (fair question) ?”.

In my professional area (academic research), that’s a very standard question to which every creator is expected to know a quick answer by heart. It may be a bit rude, but is also somewhat necessary in order to fight the NIH syndrome that all of us fall victim of regularly.


…or just providing a clarifying example of when the CNC machine falls short for those of us who have never really thought about CNC machines beyond “Hand tools are a pain. Power tools are better but very fixed-function. A CNC machine is a power tool that isn’t as fixed-function.”

Personally, I don’t see that title as rude. It’s a perfectly legitimate question!

People will find different things fun, and for different reasons. The things that people find fun in Star Wars are probably very different from the things that people find fun in Star Trek (because they are different series).

Exploring those differences can lead to a greater appreciation of both series, and it can help people to choose which series they like.

For example, if I know my friend’s tastes in movies, I can recommend a movie based upon that. Or if I was deciding whether to watch Star Wars or Star Trek, I could make that choice based upon an understanding of the differences.

I think the same thing applies to Ada vs Rust: they are different languages, with different goals. Understanding those differences can help you to appreciate them, and help you to choose which language to use for which task.


This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.