5 ms·
Having more options available in the Linux kernel is always beneficial. However, Rust may not be the solution for everything. While Rust does its best to ensure
by ysw0145 2y ago
Having more options available in the Linux kernel is always beneficial. However, Rust may not be the solution for everything. While Rust does its best to ensure its programming model is safe, it is still a limited model. Memory issues? Use Rust! Concurrency problems? Switch to Rust! But you can't do everything that C does without using unsafe blocks. Rust can offer a fresh perspective to these problems, but it's not a complete solution.
- drdo 2y agoBut unsafe blocks are available! And you should use them when you have to, but only when you have to. Using an unsafe block with a very limited blast radius doesn't negate all the guarantees you get in all the rest of your code.
- sanxiyn 2y agoNote that unsafe blocks don't have limited blast radius. Blast that can be caused by a single incorrect unsafe block is unlimited, at least in theory. (In practice there could be correlation of amount of incorrectness to effect, but same also could be said about C undefined behavior.) Unsafe blocks limit amount you need to get correct, but you need to get all of them correct. It is not a blast limiter.
- weinzierl 2y agoYes, they don't contain the blast, but they limit the places where a bomb can be, and that is their worth.
- foldr 2y agoGenerally speaking yes, but there could be a logic error somewhere in safe code that causes an unsafe block to do something it shouldn’t. For example, a safe function that is expected to return an integer less than n is called within an unsafe block to obtain an index, but the return value isn’t actually less than n. In that case the ‘bomb’ may be in the unsafe block, but the bug is in the safe code.
- Klonoar 2y agoI cannot imagine writing a method to return a value less than n, and not verifying that constraint somewhere in the safe method.
- foldr 2y agoIt’s just a simple example to illustrate the point. Realistic bugs would probably involve more complex logic. The prevalence of buffer overrun bugs in C code shows that it very definitely is possible for programmers to screw up when calculating indices. Rust removes a lot of the footguns that make that both easy to do and dangerous in C. But in unsafe Rust code, you’re still fundamentally vulnerable to any arithmetic bug in any function that you call as part of the computation of an index.
- nicce 2y ago> yes, but there could be a logic error somewhere in safe code that causes an unsafe block to do something it shouldn’t. Sounds like bad design. You can typically limit the use for unsafe for so small area than you can verify the ranges of parameters which will cause memory problems. Check for invalid values and raise panic. Still ”memorysafe”, even if it panics.
- foldr 2y agoSure, it may be bad design. The point is that nothing in the Rust language itself guarantees that memory safety bugs will be localized to unsafe blocks. If your code has that property it’s because you wrote it in a disciplined way, not because Rust forced you to write it that way (though it may have given some moral support). Let me emphasize that I am not criticizing Rust here. I am just pointing out an incontrovertible fact about how unsafe blocks in Rust work: memory safety bugs are not guaranteed to be localized to unsafe blocks.
- neysofu 2y agoI believe this is technically true, but somewhat myopic when it comes to how maintainers approach unsafe blocks in Rust. UBs have unlimited blast radius by definition, and you'll need to write correct code in all your unsafe blocks to ensure your application is 100% memory-safe. There's no debate around that. From this perspective, there's no difference between a C application and a Rust one which contains a single, incorrect unsafe block. The appreciable difference between the two, however, is how much more debuggable and auditable an unsafe block is. There's usually not that many of them, and they're easily greppable. Those (hopefully) very few lines of code in your entire application benefit from a level of attention and scrutiny that teams can hardly afford for entire C codebases. EDIT: hardy -> hardly (typo)
- drdo 2y agoThat is of course correct. The main value is that you only have to make sure that a small amount of code surrounding the unsafe block is safe, and hopefully you provide a safe API for the rest of the code to use.
- CraigJPerry 2y agoI’d word that different- it reduces the search space for a bug when something goes wrong but it doesn’t limit the blast radius - you can still spectacularly blow up safe rust code with an unsafe block (that no aliases rule is seriously tough to adhere to!) This is definitely a strong benefit though.
- deleted 2y ago[deleted]
- bilekas 2y ago> Concurrency problems? I have to admit, while I do enjoy rust in the sense that it makes sense and can really "click" sometimes. For anything asynchronous I find it really rough around the edges. It's not intuitive what's happening under the hood.
- asyx 2y agoI really hate async rust. It's really great that rust forces you on a compiler level to use mutexes but async is a disease that is spreading through your whole project and introduces a lot of complexity that I don't feel in C#, Python or JS/TS.
- John23832 2y agoEh, syntactically async rust is the exact same as C#. It's all task based concurrency. Now, lifetimes attached to function signatures is definitely a problem.
- colejohnson66 2y agoNot really. C#'s Task/Task<T> are based on background execution. Once something is awaited, control is returned to the caller. OTOH, Rust's Future<T> is, by default, based on polling/stepping, a bit like IEnumerable<T> in C#; If you never poll/await the Future<T>, it never executes. Executor libraries like Tokio allow running futures in the background, but that's not built-in.
- John23832 2y agoI don't want to "well actually" the "well actually", but I think you missed the word syntactically. > C#'s Task/Task<T> are based on background execution. Once something is awaited, control is returned to the caller. Async/await in any language happens in the background. What happens during a Task.Yield() (C#)? The task is yielded to the another awaiting task in the work queue. Same as Rust. > OTOH, Rust's Future<T> is, by default, based on polling/stepping, The await syntax abstracts over Future/Stream polling. The real difference is that Rust introduced the Future type/concept of polling at all (which is a result of not having a standard async runtime). There is a concept of "is this task available to proceed on" in C# too, it's just not exposed to the user and handled by the CLR.
- tialaramex 2y ago> But you can't do everything that C does without using unsafe blocks For this particular work the huge benefit of Rust is its enthusiasm for encapsulating such safety problems in types. Which is indeed what this article is about. C and particularly the way C is used in the kernel makes it everybody's responsibility to have total knowledge of the tacit rules. That cannot scale. A room full of kernel developers didn't entirely agree on the rules for a data structure they all use! Rust is very good at making you aware of rules you need to know, and making it not your problem when it can be somebody else's problem to ensure rules are followed. Sometimes the result will be less optimal, but even in the Linux kernel sub-optimal is often the right default and we can provide an (unsafe) escape hatch for people who can afford to learn six more weird rules to maybe get better performance.
- mjburgess 2y ago> That cannot scale. lol... you're talking about the linux kernel, written in C. The vast majority of software over many decades "bottoms out" in C whether in VMs, operating systems, device drivers, etc. The scale of the success of C is unparalleled.
- dxroshan 2y agoI agree with you.
- pjc50 2y agoThe scale of C adoption is certainly unparalleled over the past 40 or so years, but so are the safety issues in the cyberwarfare era. https://www.whitehouse.gov/oncd/briefing-room/2024/02/26/press-release-technical-report/ https://www.whitehouse.gov/oncd/briefing-room/2024/02/26/pre... If, somehow, we'd got to an era where (a) operating systems were widely deployed in a different language, and (b) the Morris Worm of 1988 had happened due to buffer overflow issues, then C in its current form would never have been adopted.
- mjburgess 2y agoC is just convenient assembly. In an era where performance mattered, and much software was written for hardware, and controlling hardware, it's hard to see an alternative. C's choices were for performance on hardware-limited systems. I don't really see what other ones made sense historically.
- pjc50 2y ago> But you can't do everything that C does without using unsafe blocks How much of this is actually 100% unambiguously necessary? Is there a good reason why anything in the filesystem code at all needs to be unsafe? I suspect it's a very small subset needed in a few places.
- nicce 2y agoUsually avoidance of copying or moving data is the primary reason. In filesystems, this is quite highlighted.
- deleted 2y ago[deleted]
- bigstrat2003 2y ago> But you can't do everything that C does without using unsafe blocks. Rust can offer a fresh perspective to these problems, but it's not a complete solution. It's true that you need to have unsafe code to do low level things. But it's a misconception that if you have to use unsafe then Rust isn't a good fit. The point of the safe/unsafe dichotomy in Rust is to clearly mark which bits of the code are unsafe, so that you can focus all your attention on auditing those small pieces and have confidence that everything else will work if you get those bits right.