arendjr

joined 9 months ago
[–] arendjr 6 points 8 months ago

Thanks, that solves it indeed!

[–] arendjr 5 points 8 months ago

Some programming languages are blindly deemed “safe” in spite of supporting unsafe memory management strategies, and somehow not enforcing those rules does not render them unsafe.

You’re applying a strawman argument here, because nobody is blindly deeming any languages as safe. But they are recognizing some languages as safer than others. Safety isn’t a binary switch, and most people recognize that. Most people also recognize that using tools that make it harder to achieve safety, does, in fact, make it harder to achieve safety. And thus, if safety is important, avoiding those tools makes perfect sense.

[–] arendjr 5 points 8 months ago* (last edited 8 months ago)

I would still like to take a moment to answer your specific questions more directly:

And what does that exclude that C or C++ has that’s memory unsafe? I suppose use after free?

I think indeed use after free is the main one, although data races are another. Both are prevented in Rust by the borrow checker.

Dereference a pointer without a bounds check is the major problem when we’re talking about memory safety.

I think that's only half of the issue, with the other half indeed being use after free. After all, using a reference isn't much different from dereferencing a pointer. But doing bounds check on all your pointers is relatively easy to do by humans; you see where the pointer is being used and you see if there's a check or not. But proving liveness of your references before you use them is much harder, because it often requires whole-program analysis to understand when the target may be destroyed. And in terms of danger, use after free is just as dangerous as unbound pointer access.

Thread safe code isn’t the issue otherwise Java, Python, etc would all be on the list of languages to run from.

Thread safe code is also the issue. The reason people don't have to run from Java is because data races there don't escalate to memory unsafety; they're just caught as "ordinary" exceptions and thus manifest as ordinary (but still hard-to-debug) bugs. But in C++ those too can create invalid memory accesses, with a possibility for exploitation. In fact, even Go has a memory unsafe data race in its threading model that occurs because of its use of fat pointers that embed both a data type and an address.

Point being, that is still a very dangerous subset. Off-by one errors have done in a lot of C code (and C++ code that isn’t using range-based loops).

It is indeed a dangerous subset, but as I mentioned elsewhere, Rust's borrow-checker, which prevents use after free with references is still active, even in unsafe code. Off-by-one errors are still bound-checked even in unsafe code, unless you explicitly use the non-bound-checked versions. Any Rust code that is valid safe Rust is just as safe when wrapped in an unsafe block. It is only when you explicitly use the unsafe features that you're on your own when it comes to safety.

A lot of these issues can be avoided in C++ by just using existing types like std::variant, std::unique_ptr, std::shared_ptr, std::array, and std::vector (with the at based accessor) instead of lower level constructs.

They indeed avoid some of the issues, but notably don't protect against use after free at all. And take std::vector as an example:

std::vector v;
v[2]; // out-of-bounds access

vs.

unsafe {
    let v = Vec::new();
    v[2]; // panic
}

Even wrapped in unsafe, the Rust equivalents are still safer.

[–] arendjr 4 points 8 months ago

I wouldn’t be so sure myself. Even unsafe Rust still uses the borrow checker, for instance. And you still get stricter checks around overflows and such as well. What unsafe does is that it unlocks the ability to use raw pointers and call other unsafe functions (among a few other things), but importantly it doesn’t disable the safety features that Rust has built-in. While unsafe Rust does indeed have some gotchas on its own, I think in general you’re still better off even with unsafe Rust than with C++.

[–] arendjr 15 points 8 months ago

I totally agree with this comment, and on top of that I would recommend anyone who really cares about the current state of affairs regarding safety in C++ to read this overview: https://accu.org/journals/overload/32/179/teodorescu/

Quote:

Personally, I am not convinced that in the near future, C++ can do something to stop this trend. C++ will leak talent to other languages (currently Rust, but perhaps in the future to Cppfront, Carbon, Hylo or Swift). If the progress towards safety started in 2015 as Bjarne suggested, the last 8 years have seen very little progress in safety improvements. Even with accelerated efforts, the three-year release cycle and slow adoption of new standards will keep C++ a decade away from addressing major safety concerns.

[–] arendjr -2 points 8 months ago (1 children)

Also, writing memory safe code honestly isn’t that hard. It just requires a different approach to problem solving, that just like any other design pattern, once you learn and get used to it, is easy.

This statement is kinda ironic after you just said it’s “easier” to just ban the entire STL and dynamic memory allocation as a whole. You already left the domain of what most consider “easy” quite a while ago.

[–] arendjr 2 points 8 months ago* (last edited 8 months ago)

Nah, 40 years ago this discussion already existed and it was between C and FORTRAN at the time. FORTRAN was often faster than C, precisely because of aliasing rules that Rust now benefits from as well: https://stackoverflow.com/questions/146159/is-fortran-easier-to-optimize-than-c-for-heavy-calculations

Btw, are these the Debian benchmarks you were referring to? Because I can see C and Rust trading blows with one another, but neither taking a consistent lead. Nothing that points to an undisputed performance lead surely. https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust-gcc.html

But I still agree with your premise that performance isn’t the deciding factor. Which means that other than legacy reasons, C (and C++) really doesn’t have much going for it anymore.

[–] arendjr 1 points 8 months ago

I agree performance is much more about architecture than language performance at the bare metal. But especially in security-conscious environments C and C++ lose in performance because architecture decisions include mitigations that need to compensate for the languages’ lack of safety. I know of several projects where C or C++ code is either delegated to separate processes with reduced permissions or to WASM sandboxes. Firefox even famously used to compile C++ code to WASM and then used a WASM ahead-of-time compiler to turn it back into native code that still maintained properties of being sandboxed. Such measures gravely impact performance however, so in those instances C is far removed from having a performance lead.

[–] arendjr 4 points 8 months ago (5 children)

One major selling point for C++ for new projects is efficiency. People are still skeptical that other languages can truly compete with C++ in that space. For example, people that need low latency (game development, trading, etc.) or are into HPC, may never be convinced to switch away from C++ because of this reason. We still don’t have enough data to argue if another language that provides safety can compete with C++ in this area.

While I can agree we don’t have sufficient data to make hard conclusions on this front, I think there are enough early indications that point to Rust being able to stay on par with or even outperform C++ in this regard:

  • Google, Firefox and Microsoft report performance improvements from migration to Rust. Some of this was attributable to having a better understanding of the problem domain upfront, thus giving an opportunity to choose better data structures. But a large part was also because choosing Rust obsoleted the need for other safety techniques that had a higher runtime impact, such as Google’s MiraclePtr or even sandboxing.
  • Crypto-traders have already massively adopted Rust as their language of choice, providing evidence the language is fast enough for their use cases. It seems a matter of time before traditional traders make the switch.
  • Rust ownership model avoids issues with possible memory aliasing that are inherent in C and C++. This enables compilers to apply optimizations they cannot apply otherwise, leading to higher theoretical throughput. Meanwhile, areas where Rust is theoretically slower (such as mandated bounds checking) are also applicable to C++ whenever safety is prioritized.
[–] arendjr 5 points 8 months ago

Generally, I would recommend against throwing in the default case, since it will actually reduce your type safety (exceptions bypass all your other types). But I learned that with another clever trick this can be avoided, giving both runtime and compile time safety: https://exploringjs.com/tackling-ts/ch_enums.html#exhaustiveness-checks

[–] arendjr 9 points 8 months ago

Great stuff! Good to see that a few of those changes wouldn’t apply to just the Rust compiler, but other Rust programs as well.

[–] arendjr 5 points 8 months ago

I can’t blame you 😅 Still, I think Bevy is quite promising and I expect they’ll get better over time. Hopefully I can provide a tiny piece to that puzzle.

view more: ‹ prev next ›