4 ms·
Thank you for this thorough and balanced explanation! This is probably why all the GUI frameworks in Rust (that I've seen) require message passing like this. Yo
by verdagon 5y ago
Thank you for this thorough and balanced explanation! This is probably why all the GUI frameworks in Rust (that I've seen) require message passing like this. You have a really good handle on the borrow checker's benefits and limitations.
I'm trying to make a language (https://vale.dev/ https://vale.dev/) that addresses the borrow checker's limitations (by making it automatic and opt-in) to handle cases like these, if you want to drop by our discord (https://discord.gg/SNB8yGH https://discord.gg/SNB8yGH), I'd love to get your thoughts!
- Jweb_Guru 5y agoI may check it out more later! From what it sounds like, Vale is an instance of option 2, arenas (which are the same as regions), but automatically managed--reducing their complexity compared to Rust--combined with implicit use of Rust's `Cell` (the interior-mutable type with the fewest compromises, in a single-thread setting). Unfortunately Rust as a language pays the price for its flexibility here. By not mandating any particular memory management or interior mutability strategy, it makes even the safe cases like this (which are relatively "easy" to validate for safety) much more complex. So if your use case is in the intersection that Vala supports, I agree this can be a great point in the tradeoff space! FYI, you might want to check out old work on the MLton optimizing compiler, which also did automatic region inference to avoid garbage collection.
- verdagon 5y agoVale uses the term "regions" a bit differently, as a way to represent a collection of objects that all are all treated the same (usually w.r.t. memory management). We have something called the "region borrow checker" which is kind of like Rust's borrow checker, but without the aliasability-xor-mutability restriction. We don't use Cell implicitly, we instead rely on "type stability" for mutable aliasing, generational references for memory safety, and scope-tethering to enable zero-cost dereferencing. Hadn't heard of MLton, checking it out now!
- Jweb_Guru 5y agoRelying on "type stability" (which I assume means no structure-altering mutations are allowed) is pretty similar to "just use atomics" as a strategy, which is what languages like Java and C# employ (it also has some similarities to the "value restriction" in languages like Standard ML and OCaml). However, this can be quite restrictive depending on your definition of type stability (and what optimizations are allowed); for instance, Rust considered changing a value to null to be a structure altering mutation and is able to optimize around that, while it sounds like Vale cannot. The rest of it sounds a lot like Rust (though I'm not sure whether regions are more like arenas or lifetimes, I'd have to look at the implementation, but generally speaking people call the same kinds of things regions even if they weren't trying to coordinate, and the performance tradeoffs you're describing sound really similar to those of regions in other languages). Overall, like I said, it sounds like a very interesting point in the tradeoff space! Edit: Also, I misspoke when I said MLton, what you actually want is the similarly named sister project MLkit (http://www.mlton.org/MLKit http://www.mlton.org/MLKit ; see http://www.mlton.org/Regions http://www.mlton.org/Regions for details on why they did not end up going with this strategy).
- verdagon 5y agoType stability lets us mutably alias, which atomics and Cell do not. It doesn't really have a Rust counterpart, a struct containing a bunch of Cells cannot be mutably aliased. If you're curious, this (draft) page has some information on it: https://vale.dev/vision/safety-2-type-stability https://vale.dev/vision/safety-2-type-stability Vale also considers changing between Some<T> and None<T> to be a type-unstable operation, and the automatic borrow checker (HGM) detects the possibility for that (because the field was marked non-final), to not do auto-borrowing for it. We might decide to use something called the Undead Cycle to help with the Opt<T> case, though it comes with interesting tradeoffs. There's a lot more to Vale, such as how our region borrow checker can enable zero-cost structured concurrency without the Send/Sync requirements, and the better RAII support. In the end, the only similarities we really share with Rust are the syntax and (hopefully!) the speed. Vale regions are tracked like lifetimes, and have a concept of immutable vs const vs readwrite to aid in optimization, but they come with an implicit interface pointer to the containing objects' allocator, similar to Odin but memory-safe. Oh, MLKit! I've looked into MLkit a bit before, but wrote them off because their regions needed a backup GC, where Vale's regions don't. Keep an eye out, we'll be posting a lot of articles to HN soon ;)