10 ms·
The background for this is that most existing UI toolkits are a poor fit for Rust. Rust doesn't like having shared mutable state, but event-based UIs have a gl
by pornel 4y ago
The background for this is that most existing UI toolkits are a poor fit for Rust.
Rust doesn't like having shared mutable state, but event-based UIs have a global event loop and can mutate anything at any time.
Rust works best with strictly tree-shaped data structures, but event handlers turn trees of widgets into arbitrary webs with possibility of circular references.
Rust prefers everything thread-safe, but many UI toolkits can't even be touched from a "non-main" thread.
Rust's object-oriented programming features are pretty shallow, and Rust doesn't have inheritance. That makes it awkward to model most toolkits that have deep hierarchies with a base View type and a dozen of Button subclasses.
So instead of retrofitting mutable single-threaded OOP to a functional multi-threaded language, there's a quest to find another approach for UIs. This change of approach has worked for games. Rust wasn't nice for "class Player extends Entity" design, but turned out to be a great fit the ECS pattern.
- kelseyfrog 4y agoIs there an ECS-compatible ontology for UI components?
- bowsamic 4y agoIn the post the author says the first architecture for Druid was ECS but they gave it up
- jchw 4y agoThe thing is, there do exist UI frameworks that prefer composition over inheritance and strictly tree shaped components where data only flows one way: they're all the rage on the web. And that's great, because they give plenty of useful insight into things that work and don't work when designing UI frameworks this way. To be fair, it's not exactly like nobody realized this. More than one Rust desktop UI framework is explicitly React inspired, and it's not like FRP-based UI was non-existent prior to being popularized in web frameworks. Still, all the same... I suspect the best answers for how to do good UI in Rust are not far away from this paradigm.
- jollybean 4y agoSo the question is, why are we wrangling our UI approach to match the strict nature of software languages? Isn't that kind of upside down? Wouldn't the obvious lack of fit hint at perhaps some kind of fundamental impedance mismatch? While most other techs are not a perfect fit, Rust is a 'worse fit' for the kinds of things we want to do in UI. The 'design objectives' of UI for the most part just do not apply at all. Even the 'Stateless / Unidirectional' tree patterns seem to be a bit inverted as I think the slight advantage of thinking just a bit about hierarchy in a different way is abnegated by the hurdles imposed when it does happen. Dart is a good language for UI, I suggest better than Rust in almost every way. The 'advantages' of Rust may present themselves in at lower layers. And both the unidirectional and even 'stateless' (i.e. Flutter) are mostly unnecessary. We'd be better served by some fairly basic conventions. I will admit I 'learned' something by using the fairly strict hierarchy in Flutter - and am better for it - but I still want my 'statefulness' tree back.
- galangalalgol 4y agoI'm pretty sure the mismatch is just that the major ui frameworks were designed for oop languages, not that UI is inherently oo. As the gp said, react type frameworks play fine with languages that have no inheritance. It's just a matter of a few more coming along with the same paradigm(s).
- sudosysgen 4y agoIt's really not inheritance that's the issue. The issue is that in Rust it's difficult to have a data flow between objects that goes in multiple opposing direction, ie, a graph instead of a directed tree. Inheritance is a way of doing that, but not the only one. Rust is capable of every OOP design pattern except ones that require this kind of dataflow inversion which become cumbersome.
- deleted 4y ago[deleted]
- 4y ago
- pohl 4y agoSwift is different from Rust, but it's more towards Rust's end of the spectrum. Sure, it has classes, but it's almost as if they're mostly there to aid in compatibility with Objective-C APIs like UIKit. SwiftUI builds UIs with value types and protocols like View. I wonder if something like SwiftUI would work well for Rust.
- raphlinus 4y agoI think the answer is yes. See rui in particular (linked by its author in another comment), which inspired my Xilem exploration.
- pjmlp 4y agoVisual designers and GUI components don't play well with borrow checker.
- pornel 4y agoSwift<>Rust similarity is only superficial in syntax. Swift has full OOP with inheritance, allows shared mutable state, and doesn't have compile-time thread safety. It has been built for Cocoa interoperability from the start. The clunky `Rc<RefCell<Object>>` that Rust tries to get rid of from UI toolkits in Swift is just `Object`. Rust's undesirable case is the Swift's built-in default (that's not a criticism of Swift, ARC works great there, but these two languages have chosen very different trade-offs).
- pjmlp 4y agoIt has partial support for it, and Swift 6.0 roadmap has a plan for it. https://www.infoq.com/news/2020/01/swift-6-vision/ https://www.infoq.com/news/2020/01/swift-6-vision/ Additionally its async/await and distributed actors story is much better than Rust's current story.
- pohl 4y agoSwift is kind of like the two-headed best Orthrus from greek mythology, but where classes & OOP are confined to one head. You only really need to deal with them when you're interacting with legacy APIs. All of the new APIs use value semantics.
- armchairhacker 4y agoI believe the solution for this is to have a shared context, batch changes and then run through all of them at once. So when you e.g. mutate a variable, it doesn’t actually change until the next frame. which you can get by calling some sort of poll function. The poll function has mutable access to your entire widget tree, applies the updates, then you can have more UI with shared access Rust is fast enough that i think performance won’t be an issue.
- jollybean 4y agoMost UI frameworks enforce a 'single thread' rule, i.e. one thread at a time touches the tree. Would this solve the problem?
- armchairhacker 4y agoyes there’s absolutely no need for UI to be multi-threaded. You can do stuff in the background but IMO multi-threaded UI is completely useless. You have an event loop where you poll for input => propagate changes => commit changes => rerender UI. Normally you might have poll => propagate => rerender, but when you delay the changes you can represent everything in a shared reference, and when commit them you have a single source which takes the 1 mutable reference Rust allows.
- kaba0 4y agoNot really UI, but wasn’t one recentish game really performant due to it using Vulkan, but in a multi-threaded way? Not sure whether it would benefit traditional UIs though.
- armchairhacker 4y agoIntensive graphics rendering is something which does deserve the GPU and multiple cores. But most "UI" as we see it shouldn't be that intensive. UI drawing is usually on the GPU but stuff like handling inputs, updating the UI tree, should be fast enough on one thread.
- epage 4y agoI'm glad Rust* is forcing us out of a rut in UI toolkit design. All of these spell hard-to-debug problems. * Yes, I know elm-derive UIs are also a thing and have laid a lot of ground work in this area. btw I've had great experience 10 or so years ago doing composition-based UI design in GTK with Python. When Maemo switched from GTK to Qt, the requirements on inheritance were one of my points of frustration and I got away with creating a couple adapter classes to avoid most of it.
- stu2b50 4y agoI wouldn't give Rust or Elm the credit for that, though. Although it was not first, React is really what put the declarative method for UI programming on the map. Today, there's a solid argument that by usage numbers React is the most used UI framework today, and it's children like Vue are high up there too. Swift also has a similar problem and unlike Rust has a powerful backer, so SwiftUI is in many ways already a vision of what a Rust native UI framework would look like.
- pjmlp 4y agoExcept the borrow checker hinders the possibility to have components libraries, or a visual designer, unless it is RC all over the place.
- worik 4y agoAre you afraid of RC? Everything in Swift is RC, but opaquely.
- pjmlp 4y agoWith the small difference that Swift compiler is RC aware and optimizes them away, unlike Rust, first point. Second point, exactly because it is opaque, its ergonomics are way better than Rust code. No Rc, Arc, RefCell, clone(), Pin, pollution across the source code.
- 4y ago
- nine_k 4y agoThere's one "UI toolkit" that builds in top of immutable data structures, trees, and unidirectional data flow. It has also taken web UI development by storm. It's called React. I wonder if React-like approaches are going to work for Rust UI toolkits. They could even wrap some native controls, much like React does with DOM controls, or React Native with Android controls.
- deterministic 4y agoYep I agree that is the right approach. Elm is amother example of this architecture in action.
- amelius 4y agoReact is nice for high-level work. However, if you're writing e.g. a rich text editor from scratch it is probably not a good fit from an efficiency viewpoint.
- hnjst 4y agoI recently tried https://github.com/yewstack/yews https://github.com/yewstack/yews that looks very much like "React in Rust". It is web oriented through WASM compilation and integration with web technologies, Tauri can be used to bridge the gap and use it for desktop apps but it is probably an excessively complex and impure assembly for that goal.
- JediLuke 4y agoThe ‘Scenic’ GUI library written in Elixir handles all these problems very elegantly, and Elixir is completely functional (no traditional OO, classes or objects)* and immutable. *of course Elixir/erlang is actually OO done right (actor model) but that’s another story
- cjohansson 4y agoThank you for the context, this also explains why most UIs gets broken / stuck at times. Like spinners that never stops or buttons that stay disabled even though they should be enabled. Those kinds of issues happens every day even in Apple, Google and Meta products (a lot of economical power) so there must be a deeper structural cause for this and I think it boils down to that the UI frameworks used are inherently fragile and has built-in edge case issues because of this
- kaba0 4y agoI wouldn’t say it is inherent, I think it has more to do with state management - some state is inherent to the component itself, but the separation is problematic. All too often business logic gets stored in the component and vice versa.
- amelius 4y ago> Rust works best with strictly tree-shaped data structures, but event handlers turn trees of widgets into arbitrary webs with possibility of circular references. This is also the problem with closures. And why you can't do Haskell/Ocaml style functional programming in Rust. Rust has the reputation of being fast, but if you force everything into a tree, you are making big compromises from the start.
- dataangel 4y agoRust has no problem expressing things that aren't thread safe. If the UI framework is single threaded all it takes is making its objects neither Send nor Sync, which will likely be taken care of automatically by them having fields that are not.