6 ms·
My big problem with Rust is too much "unsafe" code. Every time I've had to debug a hard problem, it's been in unsafe code in someone else's crate. Or in somethi
by Animats 2y ago
My big problem with Rust is too much "unsafe" code. Every time I've had to debug a hard problem, it's been in unsafe code in someone else's crate. Or in something that was C underneath. I'm about 50,000 lines of Rust into a metaverse client, and my own code has zero "unsafe". I'm not even calling "mem", or transmuting anything. Yet this has both networking and graphics, and goes fast. I just do not see why people seem to use "unsafe" so much.
Rust does need a better way to do backlinks. You can do it with Rc, RefCell, and Weak, but it involves run-time borrow checks that should never fail. Those should be checked at compile time. Detecting a double borrow is the same problem as detecting a double lock of a mutex by one thread, which is being worked on.
- deleted 2y ago[deleted]
- Ygg2 2y ago> My big problem with Rust is too much "unsafe" code. I hear cargo-geiger is useful identifying such crates. > I just do not see why people seem to use "unsafe" so much. Because it's: A) fast (branchless access) B) fast (calling C libs or assembly) C) fast (SIMD) D) people think unsafe Rust is easier Want to write a JSON parser that will gobble up gigabytes per second? Your only way is removing as many branches and use assembly as much as possible. Doubly so on stable! I guess the same goes for making a "blazingly fast"™ graphical stack. People that think unsafe is easier, shouldn't be writing unsafe code. Writing unsafe code correctly is like juggling burning chainsaws. Saying that's easier than using chainsaws is moronic at best. EDIT: Consider following, if each of your unsafe {} blocks doesn't contain a // SAFETY: // Detailed explanation of invariants One of the chainsaws just cut off your leg.
- johnnyanmac 2y ago>people think unsafe Rust is easier If that's their line of thought, I don't know why they simply don't use C/++. Or even C# with unsafe blocks. I know you said the same, but I wanted to reiterate that. But yes, I can see a few very specific cases where unsafe access is needed. Emphasis on "few". I think anything past some fundamentals should be at best a late optimizing step after an MVP is established. I also think, if it's not already there, that crates should be able to identify if it's "safe" or "unsafe". Same mentality where I'd probably want to rely on safe crates until I need to optimize a sector and then look into blazing fast but unsafe crates.
- pshc 2y ago> Rust does need a better way to do backlinks. You can do it with Rc, RefCell, and Weak, but it involves run-time borrow checks that should never fail. Those should be checked at compile time. It's not clear to me how rustc could detect a dangling backlink in a tree structure at compile time. Seems impossible short of adding proofs to the type system.
- o11c 2y agoIf the language actually supported a full set of memory policies it would be quite possible. Unfortunately it only thinks about are unique, an opinionated form of borrowed ownership, and a little bit about shared (weak I think is punted entirely to the library?), and not all other ownership policies can effectively be implemented on top of them. The usual thing approach would be: given two types, P and C (which may be the same type in case of homogeneous trees, but this), with at least the following fields: class P: child1: ChildPointer[C] class C: parent: ParentPointer[P] Then `p.child1 = c` will transparently be transformed into something like: p.child1?.parent = null c?.parent?.child1 = null # only needed if splice-to-steal is permitted; may need iteration if multiple children p.child1 = c p.child1?.parent = p Note that ChildPointer might be come in unique-like or shared-like implementations. ParentPointer[T] is basically Optional[UnopinionatedlyBorrowed[T]]. A have a list of several other ownership policies that people actually want: https://gist.github.com/o11c/dee52f11428b3d70914c4ed5652d43f7 https://gist.github.com/o11c/dee52f11428b3d70914c4ed5652d43f...
- SkiFire13 2y agoIs there some research that shows these are actually safe and can be reasonably checked by a compiler?
- ritcgab 2y agoIt's ugly but it's inevitable in some sense. The author should know what they are doing, and `// SAFETY:` comment is a must.
- fiedzia 2y agoNo. Nobody is going read those (because most people won't even know that some unsafe is buried 5 layers of dependencies below what they work with). The author should make reasonable effort to prove the code is working correctly (and cannot be abused) by other means if possible. It might be a domain issue, so far all my apps are 100% safe (not counting libraries).
- n_plus_1_acc 2y agoUnsoundness is considered a bug and should be reported and fixed.
- nneonneo 2y agoYou put those safety comments in two places: when you declare an API as being unsafe, and when you use an unsafe API. Not coincidentally, those are the two places in the language that force you to use the “unsafe” keyword. If you declare a safe API that uses unsafe under the hood, it’s on you to ensure that it’s safe to call under any situation, so that callers can call it without worrying that their program is suddenly unsafe. If you can’t guarantee that your API is safe under all circumstances, you need to declare it unsafe and make it the caller’s job to use it safely.
- hyperbrainer 2y ago> I just do not see why people seem to use "unsafe" so much. SIMD seems to be a big one.
- Const-me 2y ago> I just do not see why people seem to use "unsafe" so much Because it’s impossible to implement any non-trivial data structures in safe Rust. Even Vec has unsafe code in the implementation to allocate heap memory. When you need efficient trees or graphs (I doubt any non-trivial software doesn’t need at least one of them), unsafe code is the only reasonable choice. C++ does pretty much the same under the hood, but that’s OK because the entire language is unsafe. C# has an unsafe subset of the language with more features, just like Rust. However, it runs inside a memory-safe garbage collected runtime. Even List and Dictionary data structures from the standard library are implemented with safe subset of the language. Efficient trees and graphs are also trivial to implement in safe C#, thanks to the GC.
- deleted 2y ago[deleted]
- bubaumba 2y ago>> I just do not see why people seem to use "unsafe" so much >Because it’s impossible to implement any non-trivial data structures in safe Rust. Even Vec has unsafe code Hmm.. wasn't memory safety the main selling point for rust? If not the only. Now mix of two languages looks even worst than one complex. Especially taking into account that it can be paired with safe language from long list. Don't know what rust fans are thinking, but from outside it doesn't look very attractive. Definitely not enough to make a switch. Julia looked better at first, but turned out to be just a graveyard of abandoned academic projects.
- eddd-ddde 2y agoYou fan wrap unsafe implementations with safe APIs. The point is there's an explicit boundary between unsafe an safe.
- pcwalton 2y ago> Now mix of two languages looks even worst than one complex. The point is that the vast majority of code doesn't have to be unsafe. Empirically, Rust code has far fewer memory safety problems than non-memory-safe languages.
- pcwalton 2y ago> I'm about 50,000 lines of Rust into a metaverse client, and my own code has zero "unsafe". I'm not even calling "mem", or transmuting anything. Yet this has both networking and graphics, and goes fast. I just do not see why people seem to use "unsafe" so much. I agree. I rarely ever use unsafe, and only as a last resort. Unsafe code is really not needed to achieve high performance. > Rust does need a better way to do backlinks. You can do it with Rc, RefCell, and Weak, but it involves run-time borrow checks that should never fail. I think this will basically turn into provably-correct data structures. Which is possible to do, and I've long thought there should be systems built on top of Rust to allow for proving these correct. But we should be clear that something like Idris is what we're signing up for. Whatever it is, it is assuredly going to be far more complex than the borrow check. We should basically only use such systems for the implementations of core data structures.
- Animats 2y ago> I think this will basically turn into provably-correct data structures. That's kind of what I'm thinking. The basic idea is to prove that .upgrade().unwrap() and .borrow() never panic. This isn't all that much harder than what the borrow checker does. If you have the rule that the return value from those functions must stay within the scope where they are created, then what has to be proven is that no two such scopes for the same RefCell overlap. Sometimes this will be easy; sometimes it will be hard. A reasonable first cut would be to check that no such scopes overlap for a specific type, anywhere. That's rather conservative. If you can prove that, you don't need the checking. So it's an optimization.
- alfiedotwtf 2y agoI’ve been using Rust almost daily since 2015 and I’ve used unsafe twice - both when interoping with C. I don’t know what fancy things you’re doing with unsafe that you’re seeing it on a daily basis… maybe it’s a you problem
- Yujf 2y agoIt is explicitly mentioned that ubsafe is in their dependencies not their own code.
- tmtvl 2y agoI don't use Rust and I've bounced off trying to learn it 3 times so far, so take this with a mountain of salt, but... I think that Rust having unsafe is fine as there will be edge cases where the compiler can't quite work out whether some code will work fine or not and where the programmer can vouch for it. That's no problem. The issue I have with it is that the unsafe block then gets kinda swallowed by the supposedly safe wrappers around it. And there's no clear auditable trail back to it. I find that a bit surprising as I'd expect it obvious to have some kind of 'uses unsafe' declaration (annotation? I don't know what Rust calls those statements with a number sign and square brackets in front of a subroutine) which would then propagate upwards. A bit like how in Java (the most elegant and refined of all programming languages) you can use an annotation to state that a function 'throws XYZException' which then needs to be propagated up to a point where it can get handled. Not having such a mechanism feels a bit icky to me. It's like if there's spiders crawling out of your ears it's useful to know that they're coming out of your ears so you don't have to wonder 'are those spiders creeping out of my ears? Or out of my nose? Or out of my eyelids?', which would be a bit inconvenient.
- Expurple 2y agoThis annotation would be useless, because it would infect ~99% of your codebase. All IO is implemented as unsafe FFI calls or unsafe inline assembly somewhere down the stack. And even "pure" types like Box, Option, Vec have a lot of unsafe in their implementations. At some point these standard implementations just have to talk to an allocator which is implemented unsafely (obviously), or they have to construct a slice [1], etc. Would you make an exception for the standard library and why? I think, this just doesn't make sense. It's all unsafe somewhere down the stack. [1] https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html
- SkiFire13 2y agoIf you're doing graphics then you will have to use `unsafe` to interface with the OS primitives, there's no other way. Backlinks would be "nice" but they break fundamental assumptions that the borrow checker does. > Detecting a double borrow is the same problem as detecting a double lock of a mutex by one thread, which is being worked on. Is it being worked on using heuristics or formal methods?