Comment by steveklabnik
7 hours ago
I do think it reflects different priorities, but one of those differences is that from my perspective, safety and performance are not inherently at odds. Yes, sometimes it is needed, but not as much as some people seem to think. Sometimes, it also means writing code in ways that communicate things to the compiler that you may not think of if you're not used to thinking in this manner.
A lot of the ways in which the zig compiler works doesn't use pointers, it uses indices. This stuff is easier to write as safe code, not less easy.
> Roc is attempting to make a similar set of trade-offs in their compiler as Zig, so it makes sense that the author finds many shared patterns.
I do think that that makes sense, but it also doesn't mean that they have to. I am doing a compiler project that takes a lot of inspiration from Zig (as my language currently inherits some major things from Zig, and I also care a lot about compiler performance) and it's written in Rust, and does not use much unsafe code (outside of the usual suspects of FFI in the runtime, etc).
> A lot of the ways in which the zig compiler works doesn't use pointers, it uses indices. This stuff is easier to write as safe code, not less easy.
I respectfully disagree. This is only true if you view malloc as qualitatively different from a piece of code that gives you an index for a free object in an object pool.
Provided you don't ask/give back memory from/to the OS, what malloc is doing is giving you an index (pointer) into a pool of bytes, while manipulating an internal bookkeeping structure.
Use after free is just you using an index after said bookeeping structure has marked that piece of memory as available for something else (and perhaps claimed already).
If you have an array of Node structs to represent a graph (like the AST in Zig), and use indices to represent references, you have essentially zero protection in Rust that helps you with finding Node-s that have been used for something else etc.
The 'asking memory from the OS' aspect for malloc doesn't really change the safety of your language compared to this where it matters - if you do use-after-free on a page claimed by the OS, you get a segfault, which immediately tells you there's a problem, which is much better than silent corruption.
At least with malloc, you get debug allocators, or other features that can help you in this case. If you are careless with indices in an object pool, and overwrite stuff, essentially, it's up to you to figure out what went wrong and you have no tools to help you.
I fully agree that there are similarities here. But we disagree about some of the details.
> you have essentially zero protection in Rust that helps you with finding Node-s that have been used for something else etc.
The most obvious technique is generations. You can of course do that in Zig as well.
> if you do use-after-free on a page claimed by the OS,
This assumes that you're working in the context where there is an OS. That isn't always the case. Also, there are other cases than just use-after-free: for example, compilers will optimize around null pointers being UB, which can cause other problems, whereas an index of zero does not get the same treatment.
But also, again: Zig does not use malloc for its ASTs, as far as I know. It uses lists and indices. I haven't literally read the code lately myself, but I would be surprised if they went back to malloc'ing individual nodes.