Comment by GolDDranks
7 years ago
The lack of expressive power in safe Rust is indeed a problem. However, safe Rust is getting more expressive slowly but surely. Examples are non-lexical lifetimes that allow more flexibility locally and generic associated types that allow new types of generic interfaces (allowing, for example, expressing streaming iterators generically). I'm pretty sure there will be more enhancements in expressive power in the future.
More importantly, the lack of soundness guarantees in unsafe Rust is a more critical problem. This is because unsafe is the primary mechanism to supplement safe Rust when safe Rust isn't expressive enough – it's not just for FFI like you claim! That foundation needs to be rock solid, and this work is addressing that problem.
Btw. backpointers require a guarantee that the values pointed at are not moved as long as the pointers themselves are valid. Before, the compiler would ensure that things borrowed from didn't move, but that isn't enough to implement some systems with longer-living or self-referential pointers. And generally Rust doesn't guarantee that things won't move around. But there is going to be support for "pinning" values. That still doesn't get you all you need for backpointers (which have really complicated semantics if you think about it!), but again, one step of expressivity more that allows you to implement backpointers in unsafe code, but then expose a safe interface where you can get "pinned" references to the nodes.
Backpointers aren't that bad. You have two pointers, locked together by a simple invariant. That invariant has to be valid both before and after any operation on either pointer or deletion of either object. The problem is that in most languages, it's hard to talk about an invariant between two variables. But what you have to check or prove is trivial. You don't need a full theorem prover, just some standard compiler-type checks.
That's what I'm getting at here. There are only a few special cases where you really need unsafe code. Backpointers, partially initialized arrays, and foreign functions cover most of it. Yes, you can construct fancy situations like hash maps where there's some performance benefit in having complicated sparse arrays of pointers interspersed with junk. Is it worth it? Zeroing memory is cheap. Go zeros everything and doesn't seem to suffer.
The use case of Rust is memory safety without garbage collection. Keep that in mind. Try not to get too clever. Too much "unsafe", and it will break. Not just through coding bugs. Because someone will change something that was based on an implicit assumption made in the past by another.
You don't get to hand-wave away the complexity by just asserting that it is trivial. The hard part of all static analysis tools is reasoning about pointers.
> Go zeros everything and doesn't seem to suffer.
What are you referring to by this? For most CPU bound things, Go is measurably slower than Rust. If you mean that Go is successful as a language, well... that's almost like saying "why do you need structs/value types? Python allocates everything and doesn't seem to suffer". The languages have different domains/primary targets.