back
270 comments
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.

The 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.

Swift 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.

There'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.

I'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.

> 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.

Is there an ECS-compatible ontology for UI components?
Thank 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
I 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.

Rust 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.
The ‘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

I think the web really killed the idea of "native" widgets. I think the rise of web apps just got everyone used to the idea that controls are just going to look different everywhere. Even on macOS, practically every app I use has a very distinct look and set of widgets (vscode, photoshop, blender, spotify -- two of those are electron, but even the non-electron stuff doesn't look very much like a "mac" app anymore). And in windows, microsoft really killed it themselves by constantly updating their own apps with non-standard widgets that everyone else wanted to clone (I remember the longest time it seemed like the only new things of note in every new version of ms office was how the toolbars looked). I suppose it's not a terrible thing, but I do kinda miss the days of windows 2000 and classic macos where the platform had a (somewhat) uniform look and feel.
I think it's more a desire for branded UI rather than Web apps. Adobe wants their UIs to be instantly identifiable as Adobe, Slack wants to be instantly identifiable as Slack, MS Office as you said, etc. Personally, I doubt this benefits users (do you really need to market to people who already bought your product?) but I'm sure the logic is compelling to decision makers and it's cheaper to "design once, run everywhere". It also makes sense to developers that their app should look the same across OSes even though that "consistency" doesn't affect the 99% of users who only use one OS.
I'm the main developer of the AccessKit [1] project mentioned in this post. AMA. To preemptively answer one expected question, I know the project has been inactive for a while; I'm back to work on it in earnest starting this month.

[1]: https://github.com/AccessKit/accesskit

Do you have a Patreon/whatever setup, since "this is the way" per baby Yoda?
This is a really interesting project. Accessibility support is near and dear to my heart.

The readme mentions a macOS adapter prototype, but I don't see that directory. Does it exist in a different branch? I'd love to check it out – I'm pretty experienced with the Mac's AX implementation, but have basically no experience with AX on Windows...

> On macOS ... going forward, it’s likely that new capabilities and evolutions of the design language will be provided for the latter [SwiftUI] but not the former [AppKit].

I think this is vastly over-stating the technical churn of UI development on macOS.

While we're definitely seeing new UI toolkits introduced that are Swift-only (like the new Charts.framework[0]), nearly every Mac app Apple ships is written using AppKit. For example, I just verified (`otool -l PATH_TO_APP_BINARY`) that Mail, App Store, Notes, Music, Xcode[1], and Photos all link against AppKit, and none link against SwiftUI (on macOS 12.4 21F79, which I'm running).

There is, in my opinion, approximately zero chance that Apple either:

(A) rewrites all of the UI in all of their apps, replacing AppKit with SwiftUI, in even the medium-term, or

(B) starts treating AppKit apps as second-class citizens by introducing a new design language only available from SwiftUI.

Yes, we're going to keep seeing cool new widgets and features which are only available from Swift. No, the platform's design language is not going change in a way that makes AppKit apps obsolete.

[0]https://developer.apple.com/documentation/Charts

[1]I bet Xcode links SwiftUI somewhere for IDE integration, but I'm specifically referring to the UI implementation, parts of which have been in development since, like, NeXTSTEP and are certainly built using Objective-C & AppKit.

On the flip-side, it's worth keeping in mind where the puck is heading. Every "new" macOS app from Apple (eg, Shortcuts, System Settings, and even recent refreshes of old apps) has been written in either SwiftUI or Catalyst. A bunch of these still link to AppKit here and there, but it seems pretty clear that no new developement is breaking ground with AppKit.

Similar to the Carbon/Cocoa split, I'm sure we'll see AppKit supported well into the future, especially since we just passed the latest major architecture transition. But that doesn't mean AppKit isn't a dead end.

I would remind you that Microsoft disowned MFC and aggressively pushed all developers off of win32api once .net was released. Then they went about rewriting visual studio in .net and possibly the office suite as well. I hope Apple doesn’t attempt this because, in my opinion, I don’t think Microsoft’s products benefited from that churn.
Apple themselves aren't particularly quick at adopting the guidance that they give to developers. iTunes was carbon well after Apple was telling developers to stop using it
Apple clearly stated at WWDC 2022, that the best days of Objective-C and AppKit/UIKit are behind them, they even had a slide for it in case there were any doubts.
Aren't App Store and Photos Mac Catalyst apps?
> The background for this is that most existing UI toolkits are a poor fit for Rust.

Being in GUI business for almost 30+ years I shall admit that this above is true. And not just Rust but C/C++ are there too.

Real (a.k.a. practical) GUIs are multi-paradigm entities. Same GUI implementation may have React'ive alike widgets immersed into purely declarative DOM/widget tree with elements of immediate mode graphics on top or inside that.

It is just that GUI reflects complexity of real life - you cannot say that sickle and hammer are best tools for everything...

Back to Rust... As a language behind UI Rust is the worst language imaginable. That's primarily due to its strictness.

Object ownership graph in GUI is usually quite complex. yet it is dynamic and frequently contain loops. This situation is best handled by GCable languages. On-click-here-highlight-the-thing-there-and-remove-that-one.

Also each part of UI declaration needs it's own DSL: for UI structure definition, style system and logic behind the UI.

Before arriving with Sciter [1] I've tried [2] many things for UI: C++, D, Java, etc.

Conclusion: HTML/CSS/JS is the best (most flexible and multi-paradigm) solution that we've got so far. Not perfect of course but good enough. And considering existence of Sciter it can be fast and lightweight.

Practical example: RustDesk (https://github.com/rustdesk/ - remote desktop access) is using Sciter for UI layer and Rust for app logic layer. So it is using proper tool for each task.

[1] https://sciter.com [2] https://terrainformatica.com/2014/07/17/10-years-road-to-sci...

> Also each part of UI declaration needs it's own DSL: for UI structure definition, style system and logic behind the UI.

This is actually one thing I think rust has going for it, the procedural macro system makes it possible to embed reasonably arbitrary DSLs in rust.

I've been using egui->rend3->winit->wgpu->vulkan recently, with the goal of a program that will run on Windows, Mac, and Linux. It all mostly works. There's some dirty laundry revolving around full screen modes and window depth order, but it's not too bad.

egui, an immediate mode GUI on top of winit, is interesting. It works OK, but only does part of the job. It displays the GUI widgets, but doing something with them is your problem. Usually, you need each widget to have some persistent state. Managing that state is the user's problem. So is generating, queuing, and distributing events to and from the GUI elements. There's also something strange which causes some scrolled windows to vibrate between two states.

egui's default widgets are not very good looking. Light grey text on a dark grey background, super-thin scroll bars, that kind of thing. The aesthetics need work.

Overall, though, not bad. This stuff just needs to be used more. It has not had enough attention and polishing.

It's impressive that most of this works cross-platform, even cross-compiled. You don't even need a Windows machine to develop for a Windows target.

I just happened to build a little project using Slint (https://slint-ui.com/) last week and found it fairly pleasant, even though it's got some rough edges still. I liked it because I was able to cross-compile to Windows from my Mac and from my Linux based CI with extreme ease ("cargo build --release --target x86_64-pc-windows-gnu"). It supposedly has some sort of native widgets available if you have Qt installed, but I haven't pursued that yet.

It has a little meta language for describing the UI that gets compiled when you compile the code and that was nice because it catches type and syntax errors at compile time.

I almost went with Tauri, but the cross-compile story didn't seem quite as good compared to Slint. Tauri does look nice though—web UIs are more flexible than Slint at this point. But for my simple little tool, Slint really hit the spot.

I like how the ecosystem around Kotlin has approached the problem.

1. The language itself has a way of introducing compiler plugins that lets you express your own tree structures in a succint syntax (comparable to macros in Rust I guess)

2. Then they figured out most UIs are trees and came up with a Flutter-like UI tree declaration API with react-like re-renders. Thus Android Compose was born! This went mainstream because most android devs fell in love with this new way of writing GUI

3. Like flutter, they are slowly exposing Skia (underlying rendering engine) APIs as kotlin wrappers - I guess the project's name is Skiko

4. Now with all these pieces they are building a full-fledged UI toolkit that renders over every platform (including web) - Jetpack Compose

> Instead of trying to decide whether a GUI toolkit is native or not, I recommend asking a set of more precise questions: > > * Does text look like the platform native text? > > * To what extent does the app support preferences set at the system level? > > * What subset of expected text control functionality is provided? > > * Complex text rendering including BiDi > > * Support for keyboard layouts including “dead key” for alphabetic scripts > > * Keyboard shortcuts according to platform human interface guidelines > > * Input Method Editor > > * Color emoji rendering and use of platform emoji picker > > * Copy-paste (clipboard) > > * Drag and drop > > * Spelling and grammar correction > > * Accessibility (including reading the text aloud) > > If a toolkit does well on all these axes, then I don’t think it much matters it’s built with “native” technology; that’s basically internal details. That said, it’s also very hard to hit all these points if there is a huge stack of non-native abstractions in the way.

This is it! Browsers handle all of this. It's a ton of work. Many many gui kits start with just ASCII and really don't realize how deep the rabbit hole is for making all of this work.

And as always, if you haven't read them

https://gankra.github.io/blah/text-hates-you/

https://lord.io/text-editing-hates-you-too/

Raphlinus, great post as usual, thanks for sharing your knowledge, is always an interesting read.

I'm loosely following the progress of slint ui (formerly SixtyFPS), anything interesting in their approach or does it strictly matches one of your examples?

The elephant in the room for big CAD/CAM apps is dockable widgets, where (for me personally), the "standard" is how Visaul Studio does it.

Even Qt, with it's built-in widgets is not enough (e.g. no multiple widgets/windows in non-main window), but there are some extensions/libraries/hacks to go around.

Recently, few years ago, imgui got proper support for these too. And flutter has one or more design docs around it - e.g. multi-window support, which might be the stepping stone to these.

Desktop apps are always going to be important in this space. Our game editor (Radiant) relies on this functionality when you use two, three or even more monitors.

Regarding Electron, i don't understand why anyone would want to go this route. I work in finance and desktop native apps (Winforms, WPF) have far more mature libraries, better performance and time to market compared to web GUIs. With Electron, we first write web apps and then wrap inside electron and then reinvent the wheel - this is so stupid.
This seems to implicitly mean low-level GUIs, or GUI frameworks? The only mention of Tauri is 'tao the fork of winit used by'.

The article opens:

> A few times a week, someone asks on the #gui-and-ui channel on the Rust Discord, “what is the best UI toolkit for my application?” Unfortunately there is still no clear answer to this question.

I would say if by 'UI toolkit for my application' you just want a way to make a GUI app, not a game or some kind of highly native or specific interaction needs thing, just use Tauri. No idea why it's not a 'top contender', it has an order of magnitude more GH stars than Druid, for whatever that counts; I can only assume they mean lower-level toolkits. (No affiliation.)

I just think someone new or unexposed to rust's going to see this and think it's way harder than it needs to be or is just to do something basic.

Isn't all the discussion about the top level UX a bit of a red herring?

Iced, slint, sycamore, Dioxus , yew ... they all have quite different but very workable API surfaces. Yes, three of those target the browser, but there's nothing stopping them from building on a lower level Rust widget system instead. Even gtk-rs found a solid way to map a class structure to Rust extension traits.

Isn't the real problem in the weak fundamentals? Solid window and input handling, text rendering, layouting and 2d drawing libraries, ...

I see all the UI attempts struggling with those, and either implementing their own solutions or battling the existing ones (like winit).

I feel like if all those pieces were in place decent solutions could emerge.

Fwiw, I've head a great time with rust qmetaobject for some (basic) GUIs. Qt is more polished than any non-concerted (or even concerted) effort is going to be in at least half a decade
I attempted to implement my own windowing library after studying winit and SDL2.

The missing feature I need is supporting detachable tabs (like what chrome and sublime text have).

I think for productivity apps, this is very important. But implementing a windowing lib from scratch is not easy.

This advice is applicable to anyone building cross-platform UI toolkits, not just to Rust.
On Windows, the graphics story is more or less OK. The OS includes Direct2D and DirectWrite user-facing APIs.

It’s now possible to implement an equivalent on all modern platforms which have a 3D GPU. An open-source example in C# https://github.com/Const-me/Vrmac#vector-graphics-engine That particular one requires GLES 3.1, but I’m sure (did it before) it’s also possible to implement comparable stuff on top of GLES2.

Waiting for target devices to have TFlops of FP32 performance, and good support for compute shaders, is less than ideal. Many currently sold phones, tablets, laptops, and even some desktop PCs, have limited GPGPU capabilities and/or performance. These currently sold devices gonna stay in use for at least couple years in the future.

Rendering things on CPU is less than ideal, because many devices have high-rez displays yet relatively slow CPU and especially RAM.

Am I missing something? What’s the reason there’re no cross-platform GPU-first 2D graphics libraries, despite GLES2 (or better equivalents) is now universally available, and have been for years?

> What subset of expected text control functionality is provided?

Missing from this otherwise great list, is basic system-standard text navigation and editing. I get so annoyed when basic stuff like ctrl+end or ctrl+v doesn't work like they should.

Small comment: It's possible to stitch together UI/video/3D without dealing with the compositor, if you use child windows instead (or in wayland terms, a subsurface). On win32 at least, it's a much simpler approach.
I haven't looked closely at the latest rust gui projects, but from what I remember most of them seem to be focused on code driven ui layouts, which I think is going to be a non-starter for anyone doing serious application work. View hierarchies and layout rules are too big and too complex to be maintainable via hand coding. Even a moderately sized app can have hundreds of custom views or components that need constant refactoring. You really need something like xml or json or whatever to build out that tree structure and easily visualize it.
> while Electron continues to gain momentum

Electron is not gaining momentum, it already is the standard.

No matter what people try to invent, it needs to do all Electron does but ways better.

Bit of a hot take, but I basically expect that most new Rust GUI projects will be written in Tauri before long.

It works on multiple platforms, and is flexible, and it's easier to find off the shelf pieces due to the web technology involved, and it's lighter than Electron. It's a no-brainer for me (and others, I think) if I'm starting a new GUI desktop project.

The comparison of React Native to toolkits like Java’s AWT is incorrect given react native orchestrates the platform native UI toolkit, so the platform’s widgets will work similarly to other applications.
What’s actually wrong with electron, or makes electron slow? I feel like every ‘UI frameworks suck’ posts glosses over this, but no real indication of why.
I somehow have a hunch that actors could be a good fit for the graph vs tree problem.

Maybe it is time for some experiments

Why not copy the design of Flutter?

Why reinvent the wheel?

I know this is going to be a controversial opinion considering how much everyone seems to love Rust, but does anyone else find Rust incredibly painful to work with, even for simple tasks? Like I'm no stranger to unmanaged languages, and to some extent I cut my teeth on C, but doing anything with Rust always feels like the most aggravating exercise in needless verbosity and the "Lombok problem" doesn't help either. (Funnily enough, both these reasons are why I don't like Java either). I don't want to sound too negative, Rust is a very promising language, but even seven(!) years later it still feels like a pre-alpha language.
I have no business related to Rust, but after reading a few articles from this author, I tend to think that his approach to GUI toolkit design is deeply flawed.

He is undoubtedly highly knowledgeable about the subject, but this knowledge may be a curse in his case.

A mix of second system syndrome and Architecture Astronaut, trying to satisfy too many constraints can be a deadlock.

I think the sensible approach is starting with a Minimum Viable Product, cutting some corners and consciously making tradeoffs. And if success comes, then try to organically grow from there.