4 ms·
If using indices is going to be your answer, then it seems to me you should at least contend with the OP's argument that this approach violates the very reason
by timmytokyo 1y ago
If using indices is going to be your answer, then it seems to me you should at least contend with the OP's argument that this approach violates the very reason the borrowchecker was introduced in the first place.
From the post:
"The Rust community's whole thing is commitment to compiler-enforced correctness, and they built the borrowchecker on the premise that humans can't be trusted to handle references manually. When the same borrowchecker makes references unworkable, their solution is to... recommend that I manually manage them, with zero safety and zero language support?!? The irony is unreal."
- mmoskal 1y agoI think this is like unsafe - most of your code won’t have it, so you get the benefits of borrow checker (memory safety and race freedom) elsewhere.
- sundarurfriend 1y agoAn important saving grace that `unsafe` has is that it's local and clearly demarcated. If a core data structure of your program can be compared to `unsafe` and has to be manually managed for correctness, it's very valid to ask whether the hoops Rust makes you jump through are actually gaining you anything.
- nemothekid 1y ago>OP's argument that this approach violates the very reason the borrowchecker was introduced in the first place. No it doesn't. I just don't think author understands the pitfalls of implementing something like a graph structure in a memory unsafe language. The author doesn't write C so I don't believe he has struggled with the pain of chasing a dangling pointer with valgrind. There are plenty of libraries in C that eventually decided to use indexes instead of juggling pointers around because it's much harder to eventually introduce a use-after-free when dereferencing nodes this way. Entity component systems were invented in 1998 which essentially implement this pattern. I don't find it ironic that the Rust compiler herds people towards a safe design that has been rediscovered again and again. The borrow checker was introduced to statically verify memory safety. Using indices into graphs has been a memory safe option in languages like C for decades. I find his argument as valid as if someone said "I can't use goto? you expect me to manually run my cleanup code before I return?" Just because I took away your goto to make control flow easier it doesn't make it "ironic" if certain legitimate uses of goto are harder. Surely you wouldn't accept his argument for someone arguing for the return of goto in mainstream languages?
- loeg 1y agoIndices can be dangling in almost exactly the same way as pointers. Worse, it's easier to accidentally use-after-free clobber some other item in the same structure, because allocations are "dense." (Pointer designs on systems where malloc/free is ~LIFO experience similar problems.)
- burntsushi 1y agoExcept for the fact that one leads to undefined behavior and the other doesn't. This is a massive difference.
- bombela 1y agoThis might not seem obvious. So allow me an attempt at expanding a bit. The difference is that a dangling raw pointer to the heap will point to anything that can be modified at any time. But indexing a dynamic array guarantees that every elements is always well formed and safe to use.
- loeg 1y agoIt's true that the array approach cannot suffer from type confusion.
- loeg 1y agoNon-UB data structure corruption and other incorrect behavior isn't like, super obviously better than UB corruption and other incorrect behavior.
- conradludgate 1y agoThe obvious upside is that it's so much easier to debug when there's no UB. Debugging UB is never enjoyable.
- deleted 1y ago[deleted]
- burntsushi 1y agoThe OP's argument is bunk. It's been said many times too over the years. The fact is that the index approach does not give up everything. The obvious thing it doesn't give up is safety. It's true you can still get bugs via out of bounds accesses, but it won't result in undefined behavior. You get a panic instead. This is how the regex crate works internally and uses almost no `unsafe`.
- Groxx 1y agoThat seems rather easy? There's still plenty of safety. It's not an `unsafe` trick or worse, you still have bounds checking and safe concurrency and well-defined behavior, and you can trivially guarantee that (while mutating the vec) it cannot be changed unexpectedly while you hold ownership of the vec. The extreme ease of making that guarantee is kinda the core of their complaint. The dangling-pointer equivalent is an issue, but it's still safe (unlike in C-like langs) and there are ways to mitigate the risk of accidental misbehavior (e.g. generational pointers, or simply an ID check if you have a convenient ID). That's quite a bit different than what you get in almost any other widely-used language - e.g. some will at best be able to claim "concurrent access won't lead to undefined behavior" via e.g. the GIL, but not prevent unexpected modification (e.g. "freeze" doesn't deep-freeze in the vast majority of languages).