6 ms·
So, it's true that unsafe code can depend on preconditions that need to be upheld by safe code. But using ordinary module encapsulation and private fields, you
by lambda 2y ago
So, it's true that unsafe code can depend on preconditions that need to be upheld by safe code.
But using ordinary module encapsulation and private fields, you can scope the code that needs to uphold those preconditions to a particular module.
So the "trusted computing base" for the unsafe code can still be scoped and limited, allowing you to reduce the amount of code you need to audit and be particularly careful about for upholding safety guarantees.
Basically, when writing unsafe code, the actual unsafe operations are scoped to only the unsafe blocks, and they have preconditions that you need to scope to a particular module boundary to ensure that there's a limited amount of code that needs to be audited to ensure it upholds all of the safety invariants.
Ralf Jung has written a number of good papers and blog posts on this topic.
- uecker 2y agoAnd you think one can not modularize C code and encapsulate critical buffer operations in much safer APIs? One can, the problem is that a lot of legacy C code was not written this way. Also lot of newly written C code is not written this way, but the reason is often that people cut corners when they need to get things done with limited time and resources. The same you will see with Rust.
- gf000 2y agoEven innocent looking C code can be chock-full of UBs that can invalidate your "local reasoning" capabilities. So, not even close.
- wavemode 2y agoCare to share an example?
- capitainenemo 2y agosorting floats with NaN ? almost anything involving threading and mutation where people either don't realise how important locks are, or don't realise their code has suddenly been threaded?
- masfuerte 2y agoint average(int x, int y) { return (x+y)/2; }
- throwaway2037 2y agoI assume you are hinting at 'int' is signed here? And, that signed overflow is UB in C? Real question: Ignoring what the ISO C language spec says, are there any modern hardware platforms (say: ARM64 and X86-64) that do not use two's complement to implement signed integers? I don't know any. As I understand, two's complement correctly supports overflow for signed arithmetic. I might be old, but more than 10 years ago, hardly anyone talked about UB in C and C++ programming. In the last 10 years, it is all the rage, but seems to add very little to the conversation. For example, if you program C or C++ with the Win32 API, there are loads of weird UB-ish things that seem to work fine.
- steveklabnik 2y ago> Ignoring what the ISO C language spec says, are there any modern hardware platforms (say: ARM64 and X86-64) that do not use two's complement to implement signed integers? This is not how compilers work. Optimization happens based on language semantics, not on what platforms do.
- jandrewrogers 2y agoAt least in recent C++ standards, integers are defined as two’s complement. As a practical matter what hardware like that may still exist doesn’t have a modern C++ compiler, rendering it a moot point. UB in C is often found where different real hardware architectures had incompatible behavior. Rather than biasing the language for or against different architectures they left it to the compiler to figure out how to optimize for the cases where instruction behavior diverge. This is still true on current architectures e.g. shift overflow behavior which is why shift overflow is UB.
- oneshtein 2y agoAI rewrote to avoid undefined behavior: int average(int x, int y) { long sum = (long)x + y; if(sum > INT_MAX || sum < INT_MIN) return -1; // or any value that indicates an error/overflow return (int)(sum / 2); }
- pests 2y agohttps://www.ioccc.org/years.html https://www.ioccc.org/years.html
- nicoburns 2y agoYou're a lot more limited more limited to the kinds of APIs you can safely encapsulate in C. For example, you can't safely encapsulate an interface that shares memory between the library and the caller in C. So you're forced into either: - Exposing an unsafe API and relying on the caller to manually uphold invariants - Doing things like defensive copying at a performance cost In many cases Rust gives you the best of both worlds: sharing memory liberally while still having the compiler enforce correctness.
- uecker 2y agoRust is better at this yes, but the practical advantage is not necessarily that huge.
- lambda 2y agoThere is no distinction between safe and unsafe code in C, so it's not possible to make that same distinction that you can in Rust. And even if you try to provide some kind of safer abstraction, you're limited by the much more primitive type system, that can't distinguish between owned types, unique borrows, and shared borrows, nor can it distinguish thread safety properties. So you're left to convention and documentation for that kind of information, but nothing checking that you're getting it right, making it easy to make mistakes. And even if you get it right at first, a refactor could change your invariants, and without a type system enforcing them, you never know until someone comes along with a fuzzer and figures out that they can pwn you
- uecker 2y agoThere is definitely a distinction between safe and unsafe code in C, it is just not a simple binary distinction. But this does not make it impossible to screen C for unsafe constructions and it also does not mean that detecting unsafe issues in Rust is always trivial.
- GTP 2y agoWhich is just a convoluted way of saying that it is possible to write bugs in any language. Still, it's undeniable that some languages make a better job at helping you avoid certain bugs than others.