Hacker News new | ask | show | jobs
by uecker 27 days ago
Most of this can be done just fine in practice. NULL safety is not so much an issue as people claim as trapping is safe on most implementations and then similar to a panic in other languages. If you create data types via constructors you can then make sure those are initialized. Yes, this is a convention but easy to check. The const issue is not a problem IMHO. If you violate type safety yourself then this comparable to using escape hatches in other languages. Only the never-leave-this-thread point is something where Rust truly has an advantage, but one can have abstract data types that are thread-safe in C.

This is why I think a book is missing, people think C is much worse than it is because they do not understand what can be achieved.

1 comments

Thinking that C's lax null handling is fine because it traps misses the entire angle of preventing problems by construction, making invalid states unrepresentable.

Having a program potentially crash is very weak compared to eliminating the crashing code path entirely, guaranteed statically. This matters more and more as the programs grow, because series of 99% correct components will take correctness towards zero (failure probability mulitplies), while 100% correct scales.

Non-nullable types not groundbreaking. I'm just using this example, because it's basic enough that C has a relatable counterpart. Although some values are truly optional and Rust still has to deal with those, having &/&mut references that are never-ever NULL (and never dangling, never uninit, never misaligned, never unexpectedly mutated) makes this problem go away for all the code paths using them (which is the majority in practice).

But C completely lacks the real good stuff in Rust, like Send/Sync. You can shake some bugs with tsan, and have a static analyzer try to figure out effects and propagate them across functions, but like all analysis in C, it's hampered by lack of explicit info in the code (heuristics causing false positives), difficulty of performing true whole-program analysis, and dynamic code being a dead-end (unlike Rust where the traits are part of function types). Until you try it, you won't know how awesome it is to write thousands of lines of pervasively multi-threaded code, and have it work on the first try, without a single UAF or data race (and before you say what about deadlocks and higher-level logical races - there are library building blocks which usually prevent these too).

> people think C is much worse than it is because they do not understand what can be achieved.

I've been writing for ~25 years, from microcontrollers to compilers. Rust for 11. I think I have very good understanding of what is possible. C is stuck in 1970s. Rust at least advanced us to 1990's :)

I think it's much more common for people to not grasp how far Rust is ahead, and think it's merely a slight refinement of "be careful, use tooling" approach in C, rather than a shift towards a level correctness never seen in C.

I think Rust helps a lot for people who would other struggle because they would otherwise create a mess by enforcing certain high-level structure that rules out certain problems. But you can just use very similar structures yourself in C programs! You can prevent problems in C as well by construction, even when the compiler does not enforce it. Often you can build data types where the interfaces enforces it. And my C programs also usually work on first try! I do not have to switch to another languages to experience this.

Sure, compiler enforcement can help, but the advantages of it are massively exaggerated. The "stuck in the past" narrative I heard very often, with OO, with managed languages, etc. I would say that Rust goes in the wrong direction with monomorphization and with safe but too rigid and simplistic rules.

And the "never seen level of correctness". Just look at some of the bugs found in Rust, it is just the same old nonsense as everywhere. The favorite bug I use as an example is this: https://github.com/advisories/GHSA-5gmm-6m36-r7jh A language where you can get the trivial check for the dimensions of a matrix transpose wrong just like in C is not at another level of correctness. Or the famous cloudflare bug where some unhandled .unwrap brought the whole internet down. That you theoretically can prevent all this with the type system is great, but IMHO misses the point.

> But you can just use very similar structures yourself in C programs! You can prevent problems in C as well by construction, even when the compiler does not enforce it.

Enforcement is the point. Otherwise you're just saying people should be writing bug-free programs. "By construction" doesn't mean it's possible to construct a program satisfying a requirement, but that the compiler ensures it's impossible to violate it.

Even for a hypothetical perfectly diligent flawlessly executing programmer it's a time saver. If you want to run a 3rd party function across threads: in Rust, the compiler tells you if it's thread-safe or not, tracing through all dependencies, callbacks, checking data structures, mutexes, ref counts, etc.

And it works the other way, if I change a function that used to be thread-safe to do something unsynchronised, it won't compile any more in users' multi-threaded programs, instead of silently screwing them. Their code may have been perfectly correct before the change, and I couldn't feasibly track down everyone using my function before making a change, but having invariants in the type system automates this checking and communication.

> Just look at some of the bugs found in Rust, it is just the same old nonsense as everywhere

You're comparing your perfect self who writes flawless C on the first try with some random guy's library.

Properly designed Rust written by flawless Rust programmers is also bug-free.

Language safety is a human problem (exists to catch mistakes), and it's hard to sensibly argue whether your imaginary average C programmer is better than mine.

Rust's claims aren't absolute, so aren't disproven by a single counter-example (https://infosec.exchange/@littlealex/116838684458539473)

It's better to look at broader data, e.g. https://github.com/rust-fuzz/trophy-case - look how much lower typical severity is there.

And you can look at data from Google where they track drop in defect density in Android as it gets rewritten in Rust. I know all these top engineering companies just aren't as flawless as you are, and that's okay, Rust is for the rest of us.

Rust isn't perfect, this is my point. But its marketing is based on this idea ("rule out whole class of bugs"). If this were true then the bugs I cited could not exist, so they severe as counter examples that clearly invalidate this exaggerate claim. So we are back to the question to much incremental improvement it brings. The arguments you bring are essentially the same we heard before for object-orientation and other ideas. I agree that compiler enforcement helps, but the idea that this is a game changer is wrong.

I don't quite trust google's numbers and there are so many ways this could be biased. It is also not based on a comparison to modern C. One also has to realize how incredibly large the amount of C code is, which exists and with a lot also being very old and neglected. Any kind of CVE counting is highly misleading if you compare a new language with a security focus against existing projects. With more Rust code we will see more issues, especially when more average programmers use it that cut corners once they need to get things done. But I agree that this is where Rust can improve the situation, but so can modern C tooling. Wanting to put everything into the type system is a trend, but I don't think it is obviously the right step in the long run.