Downsides of C language culture?
Downsides of C language culture?
Posted Aug 23, 2024 14:21 UTC (Fri) by viro (subscriber, #7872)In reply to: Downsides of C language culture? by fishface60
Parent article: A review of file descriptor memory safety in the kernel
Posted Aug 23, 2024 17:16 UTC (Fri)
by NYKevin (subscriber, #129325)
[Link] (3 responses)
* This general category of work has to be done (because otherwise, your code is buggy).
Posted Aug 23, 2024 19:33 UTC (Fri)
by viro (subscriber, #7872)
[Link] (2 responses)
Rust type system is interesting in some respects; in any case, C type system is pretty weak. Memory safety is obviously a desirable property of program (and it's a property of program, not of a language), and so are correctness proofs in that area. And automating some parts of those proofs is obviously useful - to an extent determined by how good your build coverage is, which is a bloody serious caveat for the kernel. The hard part is to find out _what_ to verify. And that is language-independent; you need to reason about the things done by the program to come up with invariants and predicates that need to be verified. And if you end up with "OK, it's actually safe here, but the reasons are seriously convoluted", you need to come up with some way to massage the damn thing to make the proof of safety more straightforward, etc.
What's more, you need to be able to follow the proof; without that "compiler will catch any violations" is worse than useless when it comes to the changes you (or somebody else) will be doing several years down the road. If compiler warnings become a black box, or worse, an oracle to be appeased, you are fucked; cargo-culting will follow, and you'll find out that memory safety is not the only thing that can go wrong in code where nobody can tell you why such and such thing is done.
Choice of syntax, etc., to be used for automating the verification is secondary. "It wouldn't be needed with Rust" is asinine - the PITA would be reordered, but it would still be there. All of it. Including coming up with sane semantics for objects, figuring out how to use them safely, etc. And you would not even get the benefit of having all of that done upfront, before there's enough users of that thing to make things painful - even leaving aside the question of how much of a benefit it is, imagine a perfectly memory-safe set of primitives. Refcounted struct file, fget() taking an integer and returning a cloned reference or none and fput() dropping a reference. Memory safety is obviously not an issue for users of that API; descriptor table modifications need to play safe wrt fget(), but that's also not hard to do. All of that is perfectly doable in Rust. Now, at some point somebody points you to noticable overhead on real-world loads, with cacheline ping-pong on refcount modifications found to be contributing a lot.
Now, you notice that considerable part of that could be handled by a switch from cloning to borrowing. Again, perfectly fine for Rust, innit? Except that then you need to figure out how many of the existing users of old API could be converted to the new one and what discipline is needed to keep the things safe afterwards. And _that_ will take exact same kind of analysis. On codebase already in Rust.
Posted Aug 24, 2024 0:39 UTC (Sat)
by roc (subscriber, #30627)
[Link]
It all depends on how much of the API's rules can be enforced by the Rust type system. If all of them, then converting users to the new API is a lot easier than with C: just try a mechanical replacement; if the compiler is OK with it, you're done, and the compiler will keep things safe afterwards too. If none of them are, then you're not much better off than with C. If some of them are, you benefit commensurably.
A similar example that I have personally experienced a lot is using Rayon to parallelize loops, which in many cases is as simple as "replace `iter()` with `par_iter()`; if it compiles, then there are no data races, and in practice it will almost always work".
Posted Aug 28, 2024 3:37 UTC (Wed)
by NYKevin (subscriber, #129325)
[Link]
Frankly I do not understand why this is written in response to my comment. I did not make any of the claims you are rebutting, and I explicitly positioned Rust as a tool for solving problems, not an ideology.
> What's more, you need to be able to follow the proof; without that "compiler will catch any violations" is worse than useless when it comes to the changes you (or somebody else) will be doing several years down the road. If compiler warnings become a black box, or worse, an oracle to be appeased, you are fucked; cargo-culting will follow, and you'll find out that memory safety is not the only thing that can go wrong in code where nobody can tell you why such and such thing is done.
Well... this depends on what we're talking about.
For the borrow checker, the rules are pretty well defined (object must outlive references to them, mutable references may not alias anything, shared references guarantee immutability). It is not always easy to understand how the compiler is able to enforce these rules, true, but the rules themselves are very clear to everyone. If you find yourself writing unsafe code, you should know exactly what invariants you are expected to uphold (or else you should have another read of the nomicon before trying to write unsafe code). And if you don't write unsafe code... then it's not your problem in the first place.
For typestate APIs, it's usually as simple as "the foo() function needs an object of type A, you have an object of type B, so you can't call foo() until you call something else that turns Bs into As, and then you can't call anything that takes a B anymore." I'm struggling to imagine how that logic would become difficult to follow. It's just a state machine with extra type safety.
Downsides of C language culture?
* In Rust, the compiler can enforce that you either do it correctly, or pinky swear that you've done it correctly for this specific line/block of code (i.e. you have to write unsafe).
* In C, the compiler does help you to some extent, but it will not outright fail to compile if you miss a spot or do something wrong.
* Rust doesn't reduce the amount of work that needs to be done, but it does make it easier to confirm that you've done all of the work you wanted to do.
* Arguably, Rust does slightly increase the amount of work to be done, because you probably need to implement some kind of typestate API to enforce correctness, and that is probably more complex than an idiomatic implementation in C would be. But you only have to do this once (per API that you want to protect), and typestates are not especially hard to implement (in Rust). So, assuming lots of callsites that need to be checked for correctness, this is an acceptable tradeoff.
Downsides of C language culture?
Downsides of C language culture?
Downsides of C language culture?