Advice for the next dozen Rust GUIs
raphlinus.github.io
raphlinus.github.io
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.
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.
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.
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.
These patterns are quite useful, especially in stateful situations such as GUI. They help decouple observer from observable, and can give an architecture more flexibility in a lot of ways. Alas, the borrow checker isn't very amenable to that sort of style.
UI's are 'inherently' trees, data that needs to be mutated, at least some degree of self and circular referencing. All of that a bit outside of Rust scope.
More pragmatically, UI code tends to be 'wide and flat' - i.e. a ton of 'little things' and screens, mostly independent from one another, with visual elements.
You really want to be able to code fast, compile fast, 'try things' for validation. Performance is usually not a primary concern. That's contrary to Rust ethos where they trade all of that away for performance. So, it's a bit upside down in my view.
Rust UIs I can see for automotive, critical systems etc. - but even then - I suggest the UI should be disassociated from the 'critical' part.
Also - as you hint it's entirely unnecessary, I do actually believe OO is a natural match for UI because of the overlap between components.
I think it's the fact in UI you have a big chunk of state, with references all over, and sometimes doing sort of 'circular references' ... Rust code starts to have a lot of the various Vec Rc Cell Box all over the place, and then honestly 'what's the point of rust'?
And doing 'observers and events' feels unnatural, as well as the rest of the threading/stateful issues related to GUI.
UI is also about a lot of small details and so the typing->compile->quick check is something that happens possibly more in UI - you have to often 'see' the results you can't just run the test.
I like Rust, but it is a bit of 'Chinese Finger Trap' for smart people, and applying it to UI I think is basically upside down. I mean - fun to reason about and experiment with - but basically just wrong.
I wonder if someone will come up with a language that is the UI version of Rust, with sufficient checking etc.. That would be cool, though I can't even fathom what that might be.
One of the reasons why is that the notion of a GUI is ill-specified. Most ui frameworks, including the excellent ones, like QT, have corner cases where they fail. It's just that we've gotten to the point where coders kind have it in their bag of tricks to avoid these situations by defensive programming.
Until we have a proper model of graphical UI, it'll be hard to make any such language.
Think about the last time you had a weird UI bug (screen not redrawing, unexpected behavior, not able to see something that should be in scroll view, not able to scroll to something that is out of bounds, etc). Now think of the last time this happened in a terminal or REPL.
We have no model of user interface that adequately covers the corner cases. We do have a model for CLIs. That's why guis tend not to work.
Honesty though I fear in attempt to 'find' a model, we get forced upon us this FRP stuff for academic reasons of architectural purity. Despite QT's bugs, it mostly works just fine.
Many CLI programs were designed in the assumption the display size never changes, like 80x25 characters. That's not true anymore, users resize their windows, the text in the terminal may or may not stay good after that.
Terminals have a GUI state: cursor position, and current text attributes. That state may cause issues, especially when a program quits suddenly, like a crash.
I have never in my life witness a terminal crash so hard that I couldn't fix it and get back all my other state. In 100% of cases, just typing Ctrl+C, Ctrl+Z, etc, followed by 'r', 'e', 's', 'e', 't', enter seems to have always done the trick.
Can confirm, these things helped me as well.
Still, just because there's an easy workaround doesn't cancel the fact the observed behavior is a weird UI bug.
Otherwise, one could argue web apps don't have weird bugs because there's F5=Refresh key which fixes 99% of them.
Well, C and C++ also has all of these, with the former having it in a convention-only manner, where you have to learn what the author intended for every project. These are generic paradigms that are useful and often needed for low-level programs, and Rust makes their usage safe.
What I do find problematic regarding rust is that people want to apply it to everything, when it is a low level language, no matter how convenient it can look on the surface. Managed languages are more than fine for the vast majority of problems, they should not be replaced.
I’m pretty sure you are right. It seems to me that both FRP and ECS models of UI are at least as good as OO models, and a lot more adaptable to Rust than conventional OO models. But GUIs and OOP grew up together and have been deeply wedded, so non-OO UI just isn’t how people are used to thinking, for the most part.
> In Cocoa, scroll position is part of the view's state, a mere property of the view. This is simple because the UI itself is stateful.
> In React, scroll position is typically not part of the state from which we project the view. Instead this state is attached to the projection itself (e.g. a HTML node) and we are dependent on the "Memoization Map" to preserve it. So this memoization is now required for the correct functioning of the app. The "pure function" abstraction is leaking.
What do you mean when you say "this memoization is now required for the correct functioning of the app"? I agree that there can be some issues with scroll restoration in routing, but it doesn't seem like a big issue to me and certainly doesn't seem to reveal some fundamentally flawed architecture.
>it relies on memoization to preserve hidden state
It relies on the DOM to keep that state. It's not hidden, you are just not forced to deal with it or think about it.
>If you change the tree structure [...] and don't give scrollable UI items a key if they move, React will delete the scrollable DOM element [...]
Yes, if you have an array of elements, you need to give them a unique identifier. This is a fundamental property of any diff-algorithm which the virtual DOM relies on. I don't think that is unreasonable, and IMO it is not something that adds significant cognitive load.
React definitely has quirks and especially optimizing for performance can be quite complex. But out of all the GUI frameworks I have tried (including Win32, WinForms and GTK as well as other web ones), React is the most intuitive to me.
I think it's a leaky abstraction nonetheless, regardless if it causes issues in practice. But if it's not a significant issue to users, that's fine; personally I'd prefer to work in UI frameworks that avoid redundant diffing work by design (outside of cases like lists where it's difficult to avoid).
If leaky abstractions are unavoidable, we're left with debates about which leaky abstractions are justifiable given the constraints and goals of the design.
The abstraction is intentionally leak to meet a very valuable design goal: React plays well with other libraries that are manipulating the dom. This is a hard requirement for many businesses because libraries written without React in mind would often cost years to replicate.
So far as I can tell there's no reason someone couldn't apply the general approach to UIs React takes while also making it a less leaky abstraction. In a native scenario this makes a lot more sense because there's less components you might want to reuse vs the web.
The scroll position, in this case, would just be props.
However, at some point a UI framework usually encapsulates some kind of state somewhere. I'm not sure if a design exists that 100% doesn't encapsulate any internal state. (Maybe immediate mode GUIs?) Though, removing hidden state in the underlying UI components would at least solve the problem of needing to care about underlying node identity for them. But, it wouldn't solve it for components that compose them. I think this is unavoidable in a system that allows state encapsulation.
I think you could make an FRP UI framework that in fact, does not have state encapsulation, but it would be a lot more cumbersome to use, because you'd have to "bubble" the underlying state of any component anywhere all the way up the tree to the root.
That's not a problem for Rust, as evidenced by the fact that it is actually the way several frameworks work in Rust today.
I wonder if something like SwiftUI would work well for Rust.
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).
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.
You only really need to deal with them when you're interacting with legacy APIs. All of the new APIs use value semantics.
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.
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.
It's at the 'Model Tree' layer that the UI dev 'interacts with' directly that single thread is often enforced.
Multithread seems to only be reasonably possible when you have 'control' of everything and a careful approach, and when the situation calls for it.
Another problem that may be Cocoa-specific is that it can hook UI-changing observers to objects, and then these objects become unsafe to use in multi-threaded programs (including those background jobs you should be using!)
So it's not rocket science to just coordinate access to the tree.
It'd be nice to have an architectural way of doing that, but frankly, just living with the idiom ("Don't Do This") works well, and some frameworks put in runtime checks, i.e. QT will crash I think in some cases if you try to mess with the tree on the wrong thread.
* 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.
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.
Everything in Swift is RC, but opaquely.
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.
Rustc doesn't possess such knowledge about library types.
I recently spend two years with Swift: It is partly modern, but is slow, obtuse, opaque, and frustratingly archaic in places (its threading model is just awful).
Rust is hard, but is is beautiful, and it is mush more modern than Swift. Rust has an actual community (Swift has acres of astroturf). Rust does not wrap everything in a reference counted object.
If you are programming for iOS then Swift might be a good choice (but since it is not portable, you will regret it if you have success and want to port it - that is the voice of experience). For any other purpose avoid Swift. It has no reason to exist, we should let it go down with Apple wherever Apple is taking itself.
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.
*of course Elixir/erlang is actually OO done right (actor model) but that’s another story
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.
And that even before considering performance.
And a GC'd language for everything else.
I know if you're holding a hammer it's difficult to not see everything as a nail ...
GC is imo the single biggest mistake in software engineering.
And if you don't you'll have to pay for it many times over for the remainder of the products lifetime.
First of all, memory architecture is not thrown out the window with a GC, there are plenty of considerations that can be done in managed languages as well. Also, many managed languages have value types, allowing for basically every memory pattern you would use in a low-level language.
Second of all, a GC is the superior way of handling many, random-allocations with no patterns. An allocation in a modern GC will literally be an integer increment (not even atomic!), and every later step will happen on another thread in parallel, not slowing down the thread doing the work. No malloc implementation can beat it (unless it doesn’t care about freeing). For the rare case where an arena allocator is a better approach, the aforementioned managed languages with value types are there.
My point is that if you do that, well, then you've done the hard parts of living without a GC - so might as well ditch it altogether.
But the absolute vast majority of course doesn't have excellent memory architectures, so they'll suffer greatly from not being "burdened" with having to think about memory.
It is unfortunate that GC debate is all about performance. The programmer ergonomics alone makes the GC a worse choice. But then of course we still have GC tuning.
Most programs are not video decoders running a tiny tiny code on gigabytes of data, they contain quite a bit of code and many parts of it run irregularly. By not “paying attention” to memory allocations on these vast amount of cases, you get similar, or sometimes even better performance, safer and more correct software, faster. If it turns out to be a critical part, it is very easy to pay a bit more attention to the allocation story there.
So in like 90+% of cases, a GC is a huge boost to productivity, and this is proved by their extensive usage in the industry.
And in what world is a GC safer and more correct? And no, please don't argue that it is faster in any meaningful way.
> So in like 90+% of cases, a GC is a huge boost to productivity, and this is proved by their extensive usage in the industry.
It is not a boost in productivity. And the reason for why it has extensive use in the industry is because the sad state of programming languages have been that languages with GC are safe and high-level whereas languages without a GC are old and unsafe. That really has nothing to do with the GC though. Which is why I say that the GC has been the biggest mistake in software engineering.
Rust and Swift are beginning to change that in a very small way.
Swift chose a different approach, but their tradeoff was lower memory overhead vs performance. It is likely a worthwhile goal for mobile devices, but that is a niche. And by the way, for all practical purposes RC is a garbage collection algorithm, it just tracks dead links instead of live ones.
So there is one solution for correctness and safety without GC, which comes with plenty of warts. How exactly GC is not a boost in productivity?
Modern C++ works very well too, but it is of course a huge language with a lot of legacy that makes it unsuitable or undesirable for a lot of things. But again, completely orthogonal to the GC.
That's a very shallow dismissal of swift... Oh, and Chris Lattner would not agree: https://atp.fm/205-chris-lattner-interview-transcript#gc
> How exactly GC is not a boost in productivity?
You claim that every other sentence but present nothing to stand on. The interview linked above shows some perspective if you care. You are not relieved from thinking about memory with a GC.
GC is a sensible response to C-style memory management, but not to any form of manual memory management.
The problem is that the options for non-GC languages are so poor, so the most reasonable language choice gives you a GC whether you want it or not. Hence my main point.
So it's not like you pick Go because it has a GC. But you might pick Go despite it having a GC.
Well, yes and no. Sure, it helps with data races (but not with race conditions in general), but foremost it is a tool that allows for correct compile-time memory management. Compile-time memory management is only possible in a subset of programs, so rust as well has to use (A)RC at times. This is okay when used sparingly, but atomic increases are very expensive on today’s hardware.
I am familiar with RAII, but that is the exact same thing what Rust enforces at compile time, with the exact same shortcomings, so I don’t see how is it an argument against GC.
Reference counting can cause long pauses as well - as I said, it is the same problem, just looking at it from the other direction. If an object with many many references to plenty other objects die it can take quite a long time to deallocate, there are no free lunch. And then we haven’t even talked about cycles that need a similar background process to tracing GCs, without which it will leak.
Also, I have seen the interview, I believe this thread is about it: https://news.ycombinator.com/item?id=31139610
While I am all for research into better ways to do RC, please look at the discussion - it is not at all clear that RC would be better, and even theoretically a tracing GC will win.
It was an alternative, that does less and doesn't put as much restrictions on the programmer compared to the borrow checker, as you complained about rust and equaled that with not having a GC.
And it is deterministic.
>While I am all for research into better ways to do RC, please look at the discussion - it is not at all clear that RC would be better, and even theoretically a tracing GC will win.
Win what and in what sense?
How every GC language has problems with GC tuning is in my opinion a clear indicator that the GC lost. With no real benefits short term and huge downsides down the line - even if you never hit any limits.
> How every GC language has problems with GC tuning is in my opinion a clear indicator that the GC lost.
Citation required. It Just Works in like 99% of cases, and it’s not like there is any solution that covers every edge case. Just look at the thread I linked, there is an example of C++’s RC lingering for a long time after the effective work is done freeing many things.
It is an axiom at this point. GC battles are not exactly unheard of on HN. Don't worry, it gets fixed in the upcoming release - has been said for decade after decade.
No there is no solution that covers any case. But you pretty much always have to think about memory, so the appeal of using a GC when the only argument in its favor is that you don't have to think about memory is quite unclear.
* IDEs that know the language and catch syntax errors.
* Deprecating NULL. (Early days yet, but I am a believer)
* IDEs (again) and syntax highlighting.
* RAII (is that "since high-level languages"? I think so)
* Abstract interfaces.
It is a mindset about memory, but you have to have a mindset about memory anyway so it isn't exactly unique.
Keeping track of memory is one of the hardest parts of programming in languages like C, a bit easier in C++ (thanks RAII) but still memory leaks are rampant.
Rust improves in some aspects, but I do not think it is a language for inexperienced programmers.
So where garbage collection pauses are tolerable, and real time performance not an issue, save the money and use garbage collection.
I too hate garbage collectors, I want to be in control. But the business case is clear: garbage collected languages are a better choice in a lot, f not most, cases.
I agree that Rust is a professional tool, designed for professional use. It's designed for professional use, instead of being optimized for inexperienced beginners like Python is.
I just don't really see "inexperienced beginners can easily contribute productively to this code base" as something that would have been valuable for any of the professional work I've done over the past couple of decades of my career.
If you're an amateur, or if you're programming recreationally, or if you're writing some low-reliability low-impact throwaway code, sure, it's great to use happy-path-oriented languages. When you want to write something that people actually rely on to run a business, you should use professional-grade tools instead.
If that isn't hard to you, it's probably because you've gotten so used to that mental overhead that you don't notice the drag any more, or because it's worth it for your case so you don't feel it. More power to you, but the rest of programmers don't want to have to deal with that, and don't need to. They'd rather get the feature done and go home to their families.
Sure, they'll get the feature "done". And then they'll suffer for it later, adding up to much more work and a much less rewarding code base than if they'll done it properly in the first place.
When operating a Mac I expect Mac-specific behaviors. When operating a Windows PC I expect Windows-specific behaviors. I LOATHE programs that decide to do away with OS-isms that I am accustomed to and require me to learn how to "do things their way" because they wanted cross-OS consistency. Sometimes this means conforming everything to a single platform which is partially evil but just as often it means conforming to _none of them_ which is pure evil.
But I understand this - as a web developer I've experienced pressure from Project Managers and the "decision makers" to make things consistent across browsers/devices even if doing so goes against how the users of said browsers/devices would expect things to operate - breaking user expectations and creating a worse UX for many users. I fight against it when I can but I'm not always successful.
I agree that some platform specific behavior is important. But only behavior that relates to OS, like window management, system dialogs, accessibility etc.
IMHO we are focusing too much on what does and doesn't look like platform UI. Big part of native widgets is also about branded UI. Arguing weather Apple or Adobe is more important brand for the UI is kind of pointless.
Consistent UI across multiple platforms affects more than just people who are using multiple OSes. For example, user watching a tutorial, expects to be able to replicate instructors actions. Imagine the joy of discovering in-app menus on his machine are different, because instructor was using the app on a different OS. Also, greater cost of designing and developing widgets for different platforms means there is less budget left to design custom task-specific UIs with better UX.
For UI "inside" the app, we should be focusing on what is a best possible UX of human-computer interaction for a task at hand. Just blindly using native widgets for everything can actually lead to bad user experience. For example, I hate it when an app uses Win32 Color Dialog for color selection. It's a widget with a bad UX for 99% of use cases.
To me, as a user, UI with good human-computer interaction experience is much more important that use of branded platform-native widgets.
This is ahistorical. App branding now doesn’t hold a candle to the late-90’s mania for every app having its own skin engine. App-vs-platform UI deviation is a lot tamer nowadays.
I dunno, I would say the 90s (well, for Windows and MacOS, DOS and the other no-standard-GUI OS’s that were still very much alive in at least the early part of that period are a different story) were pretty much the low point in terms of degree of UI branding for “professional” apps.
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...
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.
You can examine which frameworks a binary links by running `otool -l /System/Applications/Photos.app/Contents/MacOS/Photos`. Traditional macOS Cocoa apps will link AppKit, while Catalyst apps will link UIKit.
For example, if you run that command with a Catalyst app (e.g., Messages or Maps), you'll see that those apps link against `/System/iOSSupport/System/Library/Frameworks/UIKit.framework/Versions/A/UIKit`, and AppKit is nowhere to be found.
(So does Twitter on the Mac, which was written by a developer on the original iPhone.)
Only vaguely familiar with MFC. Elaborate?
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.
(A) an app was being ported from iOS because it didn't already have a native Mac counterpart (e.g., TV, News, Podcasts), or
(B) the Mac version was significantly behind its iOS counterpart from a feature perspective (e.g., Messages).
I had written a diatribe earlier about how I don't see this as equivalent to the Cocoa/Carbon situation, but deleted it because I'm being long-winded enough already. It boiled down to the fact that Carbon existed specifically to help port apps from a legacy OS to what Apple was loudly declaring to be the future: OS X. Carbon was a way for apps to get off the sinking ship that was the "classic" MacOS. I mean, they had a funeral for it! https://i.imgur.com/KjFh63u.jpg
20 years from now, will Apple's macOS apps contain more Swift+SwiftUI than Objective-C+AppKit, probably! But I'm not worried about AppKit being a "dead end" until they rewrite Mail, Keynote, and Xcode – because they have significantly more invested in those apps than I have in my AppKit apps.
(For the record, I love SwiftUI, and use it whenever I can! I just have no expectations that AppKit is going away any time soon.)
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...
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.
At the end, main idea of initial Rust design is to be the language with what browser is implemented.
JavaScript (in ES2020 specification) is really good enough as UI automation language. If not JS then TS.
Just in case in Sciter I've added native JSX to JS. JSX is an ergonomic way of defining tree alike literals that used for UI DOM population and patching.
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.
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.
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
There are even ADB episodes speaking about all those steps.
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
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?
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.
I understand the term "dockable widgets", but I'm not sure I'm visualizing the same UI as you.
This includes everything like the exact colors and gradient stops and animation timing and vector shapes and accessibility behavior etc. of buttons and scrollbars and everything. Example: [3]
I wonder what one could learn / achieve trying to "port WPF to rust" / implement a XAML control template renderer in Rust. If you can "simply" parse and interpret those XAML files do you instantly get a native-like GUI that supports the exact look and feel of these different Windows themes? (on any OS!)
Somehow I think it is not realized how amazing that is!
[1] https://github.com/dotnet/wpf/blob/main/LICENSE.TXT [2] https://github.com/dotnet/wpf/tree/main/src/Microsoft.DotNet... [3] https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet...
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.
Unless perhaps they're also big into the JS ecosystem anyway. That 'mature tooling' you mention is npm/yarn/etc. stuff; Tauri's is cargo.
Still, the “why not use Electron?” answer is that Electron is much more hostile to the user…much larger download size, slower startup and increased memory usage. With Rust valuing zero-cost abstractions, Tauri comes a lot closer to that ethos than Electron.
It seems to me that, maybe because of the situation described in this article, Tauri already has become, or at least is quickly becoming, the clear answer to this question.
This article seems to be implicitly excluding Tauri from consideration, because the UI portion in Tauri is not "native" or even Rust-based. With Tauri, you write the whole app in Rust except the UI.
(Technically, you could use some Rust UI frontend tech, but I think 99% of devs using Tauri are using some web UI technology, such as one of the several excellent built-in vite-powered starting points (Svelte, Vue, Solid, React, Angular...) or something similar.)
This seems to me to be a pretty good solution for the next several years, at least, because implementing a good cross-platform native UI toolkit is very very hard — so hard that, arguably, nobody has ever done it.
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.
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.
For example, they don't give you the correct mouse coordinates once your mouse cursor is outside a window. I heard this is impossible to do if the underlining system is wayland.
I understand, for games, it can support extreme zooming with small textures. But for GUI (for example a text editor), we only occasionally resize fonts.
I looked at some of the open source projects (mostly terminal emulators), they simply rasterize fonts onto a texture map using CPU. I wonder if signed distance field is really necessary.
Moving a bit farther from the strictly 'text' realm there are authoring tools for vector graphics, where you would like to be able to view the changes you make in real time. CPU can do it (since it's probably the only complex thing onscreen, and changes are likely localised anyway if you really need the extra cycles), but GPU can do it better.
In 2022, smooth font rendering during pinch zoom is yet another case in which we in the software field dropped the ball, but it didn't matter because hardware picked up the slack.
And Safari should just rerasterize glyphs while zooming. In 2022 there's no technical reason why it can't.
But just letting hardware pick up the slack hurts people stuck with older hardware, right?
For font rendering, there are a lot of existing techniques out there, but whichever is best depends on your requirements. There are no silver bullets, and slug certainly isn't one.
I will say though that most applications probably don't need hardware accelerated font rendering. A software renderer like Freetype2 is going to offer much higher quality results than anything like slug ever could, and with proper caching you can achieve good realtime performance. For a real world example of a text-heavy application that does this, see Lite-XL[2]
1: The landing page doesn't even mention the word "patent", so here's some evidence: https://twitter.com/ericlengyel/status/1159917092331642880?l...
It's working on linux (with x11), but buggy on Mac.
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?
I don't know the code well enough to find good proof, but there's a lot of GLES and I think some DX calls in there. More than if it was just composing.
I'm reminded of how Windows Presentation Foundation relied on a level of GPU-based 3D acceleration that wasn't yet universally available on non-gamer PCs when Vista came out. This affected my brother when he was in seminary and tried to run the WPF-based Logos 4 Bible software on a budget laptop bought around 2007 (edit: actually 2008 IIRC). By 2011 it got bad enough that I just bought him a new laptop. And he wasn't the only one affected by this; there were others talking about it on the forum for that application [1]. This anecdote has stuck with me as a cautionary tale about us developers failing to put our users first in our technology choices (though I admit it hasn't actually stopped me from using Electron when the pressure to ship is on).
Inexact but IMO pretty good still. With some modifications, the approach can be used in GLES2.
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.
edit: btw, great article. I've worked on a cross-platform application (Windows, Linux, OSX) which went from using wxWidgets to Qt. Quite painful either way, and while Qt was a fair bit better on OSX at the time we still had to use tons of ifdefs and per-platform configuration.
And while I've been a win32 GUI programmer for decades, I totally get why people reach for Electron or embedded web servers. It's a really hard problem space with lots of trade-offs to be made.
And of course a Wayland subsurface is using the compositor.
[1]: https://docs.microsoft.com/en-us/windows/win32/directcomp/ba... [2]: https://developer.apple.com/documentation/quartzcore/catrans...
Approach is explained here: https://sciter.com/sciter-and-directx/ You see there exactly 3D, video and GUI around.
I don't see why Rust couldn't be successful with a similar approach.
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.
Tauri strikes me as a good step up from Electron, and likely a better fit for Rust users (as you can code everything except for view in Rust), but I have just started playing with it.
You can do the whole stack in Rust,
https://dev.to/stevepryde/create-a-desktop-app-in-rust-using...
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.
This is of course a problem with the Webview implementation on Windows but it is so awful it makes me want to use something else instead.
Note this is not a reflection on Tauri which I think is awesome but Microsoft really should fix it.
IIRC, WebView2 is supported going back to Windows 7...
The “use Qt/GTK/etc” is quite a common refrain on hn, and while I agree all these projects just have not achieved the DevEx that anyone is looking for.
Electron/Tauri and even flutter are easier, and that’s why I think they will win.
AFAIK the real problem is accessibility features which are lacking. The wasteful resource use will be essentially fixed as computers get faster/hold more data, and more devs put even minimal effort into resource usage.
There’s also a breed of users who are extremely sensitive to the “native feel” of a desktop app. For them, it’s a jarring experience to downgrade to something that is less snappy, but they expect a certain standard of snappiness.
Maybe it is time for some experiments
Why reinvent the wheel?
If my project requires the memory safety that rust offers, I’ll choose rust. But most of the time I’ll pick something else to lower my mental load when coding.
In my experience, it mostly does. I’m now significantly more productive in rust than C, and I feel much more pleased with the end result.
I still find it takes a lot more effort to write rust than javascript though. Both more thinking per line, and it often takes more lines of code. The resulting software is faster and more correct, but it’s not a uniform win. I still reach for javascript for “code as content” - like UI code.
Rust’s async story is awful. Pin is confusing. Async doesn’t play nice with other rust features (like traits). There’s no async streams and it’s extremely difficult to code your own. Solutions to these problems (like GAT and TAIT) have been proposed, coded and discussed for 6 years or something but they still haven’t shipped. I’m quite frustrated with how slowly the language is moving to fix obvious problems. And it seems to be getting slower as more people try to “help” (by chiming in on GitHub).
But I really like rust-sans-async as a language for infrastructure code. The stuff I’ve built with it is bonkers fast, safe and correct. The crates ecosystem is fantastic. (Much higher quality than npm). And the community is smart and lovely. It’s not the most productive language but it fits the “better C” niche really well.
But if you avoid trying to be really annoying with the type system like the majority of the ecosystem is, and slap Arc/RefCell/heap allocations everywhere, the language turns back into a high-level language again.
Unfortunately, stepping outside the realm of ``core`` and ``alloc`` and ``std`` means having to repeatedly step on overly clever landmines everywhere you go.
If you find it easy to build a linked list or graph in C, you can do it just as easily in Rust — use unsafe, and you have your easy linked list, with exactly as much safety as it had in C. Sure, it’s more challenging to build a fully memory and thread safe linked list or graph, but it’s actually hard as hell to do that in C too. Other languages make it easy to build one with these guarantees only by requiring significant runtime support, which is out of scope for Rust.
In the end, it’s pretty unrealistic to expect that any language would allow you to write a guaranteed memory and threadsafe graph structure with zero runtime overhead, without a lot of knowledge, time, and attention on your part — there are no silver bullets.
And if you’re using Rust for anything real you’re generally not doing sophomore computer science homework like this anyway.
In practice, I find that unsound libraries frequently get written and used unknowingly in the wild. I've commented on this earlier at https://news.ycombinator.com/item?id=31897503.
In short, I believe that Stacked Borrows places unreasonable and unattainable requirements on authors of unsafe structures and algorithms, which serve as the foundation for practically all safe code (outside of the vanishingly rare case of code operating on tree-shaped fixed-size variables allocated solely on the stack, and never creating aliased mutable pointers).
The rest of what your wrote is completely irrelevant to the original point: this seems hard in safe Rust only because you’re comparing it to doing something totally different and simpler in C.
Grind your axe about stacked borrows elsewhere.
You said Rust linked lists have "exactly as much safety as it had in C". Are you seriously arguing that C linked lists are just as wrong as Rust ones, by saying a CS student is as likely to write an incorrect C linked list as a hardened professional is to write an incorrect Rust linked list (eg. Tokio and https://gist.github.com/Darksonn/1567538f56af1a8038ecc3c664a...)? Stacked Borrows ensures that translating sound C code into idiomatic Rust APIs produces needlessly unsound Rust code, and the Rust language (or a specification if it existed) means C translated directly into raw-pointer Rust is unidiomatic and you're fighting the language every step of the way (no autoderef, no -> operator). At this point, an experienced programmer is more likely to write and use a correct C linked list than write and use a Rust one.
What?
No, I’m saying that writing a linked list in C is “easy” if and only if your implementation doesn’t even bother to try to cope with the 9000 foot guns C offers you — that your easy C graph data structure is basically always a disaster of Undefined Behavior and thread unsafety, because if you try to make it otherwise, it will cease being at all easy. It will become in fact really fucking hard
So when I say “you can have your easy C-style linked list in Rust, just use unsafe”, I agree entirely: the Rust implementation will also likely be a gigantic clusterfuck of Undefined Behavior. That’s the entire point: it’s only ever easy in either language if you’re willing to blow both of your feet and you dick clean off. It’s easy only if you’re so inexperienced and naive that you don’t even perceive the dangers of the highly efficient dick-and-leg removal device you’ve built in C, and whine on forums about how mean-old-Rust is hard in comparison because it keeps refusing to compile your dick_exterminator.rs file.
That’s the Apples to Apples comparison. Which unsafe thing is harder to get right, I don’t honestly give a flying fuck about. Now, it’s highly de-fucking-batable that it’s easier for “an expert C programmer” to avoid undefined behavior entirely in an arbitrary mutable graph implementation in the presence of multithreading unless we’re talking about an entirely mythical level of expert here, but that’s utterly offtopic to the discussion at hand. You were just looking to grind a fucking axe about Stacked Borrows and decided to rant at me about it, but it really has fuck all to do with anything I was saying, man.
You can have an easy graph structure in Rust in the only way you can easily have one in C: by not giving a single shit about correctness.
And C is not a dick-and-leg removal device, it's a direct representation of runtime semantics (aside from signed integer overflow which is avoidable, type-based alias analysis which is rare, etc.), and any sound Rust code which doesn't transmute types can be compiled into equally UB-free C code, and even Rust which commits UB by violating SB (many unsafe libraries) can be transpiled into UB-free C code as long as you don't use `restrict` when inappropriate. Rust is merely a possible way to organize a program to avoid UB, to be followed when helpful (RAII, catching use-after-free in application logic, avoiding reference counting errors, multithreading) and replaced when it impedes writing low-level code. It's not a religion where apostasy is punished by castration.
I'm criticizing Stacked Borrows because I've seen more than enough evidence that it's an unreasonably stringent memory model for writing unsafe code. Please stop putting words in my mouth and misrepresenting my positions as profanity-laced straw men, like "not giving a single shit about correctness".
lmao. profound stuff man.
> It's not a religion where apostasy is punished by castration.
What on earth are you even talking about. It was just a metaphor for runtime crashes you absolute dingus.
> I'm criticizing Stacked Borrows because I've seen more than enough evidence that it's an unreasonably stringent memory model for writing unsafe code
I didn’t ask! I don’t care! At no point in my life have I cared about anything less than I care about what “nyanpasu64” thinks about Stacked Borrows. Take the hint you tedious dork.
On the other end of the spectrum I would say Go is, with its lack of "advanced" or functional language features and sans-syntactic-sugar straight forward approach. I always feel like I have oncoming RSI when I write Go, its just so verbose and un-dense and frankly boring. No room for (too much) cleverness.
At the same time I would guess real development teams using Go are more productive (in problem spaces where Go can be used instead of Rust of course). Especially if you factor in mentoring of junior colleges new to the language etc.
That is mostly only true for very early beginners.
There are certain patterns you have to learn and understand, especially for developers not accustomed to thinking about lifetimes ( which applies to most developers that only have experience with GC languages).
And sometimes you do have to battle the compiler, even with experience.
But most of that goes away pretty quickly once the language clicks for you.
After that Rust can still feel restrictive, but that's because there are very few languages that enforce as much correctness at compile time.
That very well can mean that Rust just isn't a good fit for certain domains - which is perfectly fine!
Side note, Rust best practice seems to be in favor of getters and setters like Java and several popular libraries I've seen don't expose struct fields directly instead opting for set_x() and x() methods. Give it a few years and I'm sure Rust will have its own Lombok crate generating getters, setters, and constructors too.
I don’t love rust. I think rust has some fair critiques to be made of it.
It’s not, and never has, or (probably) will be the answer to all problems.
Don’t use it just because it’s popular; why are you trying to use it, and what are you using instead?
I’ll eat my hat if you can convince me that rust is more of a pain in the ass than c++. I think cpp is a stupid broken ecosystem, and the fact that rust went all in with a single unified package manager makes the comparison a non-event.
So… compare apples to apples right?
“pre-alpha” language? I don’t even know what you mean by this; but, it’s ok not to love it.
There are things I dislike about it too.
It is verbose.
I still use it though; it’s better than the alternatives for what I’m doing.
I have heard all the arguments for using Rust in GUI apps, but realistically they seem to be very weak to convince a serious team of developers to choose between the alternatives.
There's definitely a learning curve (I think everyone agrees with that one), which might be what you're feeling now, but that quickly goes away once you pick up some speed. I would say keep on learning Rust and the language will grow on you.
Can you give any concrete examples of things that give you this impression?
* Rust has no way to talk about heap allocations succinctly other than Box; an actual type I had Option<Box<[Box<[&'a str]>]>>. It's more than just Box and Option being poor abstractions for the heap and nullability respectively, but the fact that Rust is a systems programming language and provides nothing to actually help with even mundane problems that arise in systems programming
* there has been almost no iteration in the design space of lifetimes despite being a cornerstone feature of the language
* prolific do_x and do_x_mut methods; there has to be a better way than countless *_mut methods for a language where mutability is so important
My general impression of Rust is that it ships a MVP version of a feature and never really tries to iterate on it, or iteration happens incredibly slowly (const generics being the only notable exception I can think of where almost every release seems to have something to say about const generics). And I get it, things like GATs are important for the language long term, but the "ivory tower" approach has left the rest of the language feeling neglected IMO.
At this point, I kind of feel like, if you're writing safe C or C++, being mindful of where the data goes and why, you're pretty much writing Rust that compiles.
Exactly. If you're using modern c++ and you actually know what's undefined behavior and what's not, you're doing rust. The only difference is c++ compilers don't error out on the undefined behavior. Actually it wouldn't be that hard to write a c++ compiler that errored out in this way. unfortunately, it'd break all c++ programs today.
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.
I think that unfortunate early design decisions can lead to dead-ends, or extremely painful evolution, of course, and knowledge can help, but paralysis is in my opinion an ever bigger issue.
Making the perfect GUI Toolkit starts by making one that is Good Enough for some use cases.
That said, I think Druid is a good Minimum Viable Product. I just needed GPU acceleration for my app, and wanted something closer to SwiftUI, which I'm used to.
Maybe it would work if there was no other choice, but in the end you can always use bindings to GTK or some other mature non-Rust UI framework, or use web-view based option.
In order for a pure Rust solution to succeed in the general case, it needs to meet all of these constraints from the get-go, but of course that's an insurmountable task.
For that reason, I think the best approach is to not try to "solve" GUI yet, but instead focus on the pre-requisutes:
- Window creation. - Text rendering. - Compositing. - Accessibility. - etc.
ie. all of the things mentioned in the article can be separated out and tackled one at a time. For that reason I think the author has taken exactly the right approach.
I will one hundred percent agree with you that my approach is not a good one if you want a pretty good GUI in a reasonable amount of time. For that, an incremental approach, especially adapting some existing successful design, would be better. But that is not in fact my goal, it is to yearn for the sea.
I am, quite deliberately, spending a lot of risk points. In addition to using a language which may (see elsethread) not be a good fit for expressing UI at all, I'm building a GPU 2D renderer from scratch, also using compute shader techniques which have not been proven, and I am designing a reactive architecture that is not just a simple adaptation of React. Any of these could fail, as have some of my previous attempts. But I think they will be interesting failures, in that we'll learn something, and if all the pieces do come together, it will be a UI toolkit capable of performance completely untouchable by the existing state of the art. In turn, I'm interested in how that could open up new creative possibilities constrained by current implementations.
So I'm pretty comfortable with my approach. And hey, next time you're in the Bay Area (or perhaps when I'm in your neck of the woods), lemme buy you beer and we can talk about why it gives you satisfaction to dump on other people's work.
I love this response - stealing it :)
In my opinion GUI is not yet something I would consider to be a "solved problem". Both from the API perspective and the rendering side, and compute shaders are indeed extremely promising and could be something close to an end-game in this space.
This is why I read your articles.
And I have absolutely zero interest in dumping on your work, but I have the feeling that a large part of this work is going to waste because of what I consider to be flaws in the ways you are approaching the problems, or maybe the way you advertise your approach, I can sense how discouraging it can be for other devs.
On the contrary, knowing/talking too much about the difficulties can quickly kill the fun and motivation.
He should talk about how great the end-goal will be and why it is important.
Edit to add:
"Those who cannot learn from history are doomed to repeat it." --George Santayana
I think the time for recklessly moving fast and breaking things in software, without taking into account known complexity and avoiding the mistakes of the past, is over. Our impact on the world, and the resulting responsibility, is simply too great for that.
Could you give an example of an application where the bottleneck is UI code? In my experience the bottleneck is always either disk or network. Not trying to bash you, genuinely curious.
Sure, that might be technically a bandwidth problem: in this case probably RAM. It is solvable with faster computers/faster RAM. But it's slower or at least the same speed as it was in the past with slower machines. Since hardware got better, it has got to be something different in the software side.
And it's not even about difficult things like Unicode Glyphs and Emojis, which are common canned responses when anyone says that software "is slower than N years ago". Those things are handled by the OS, not by Jira. And there are super fast apps that make use of them.
I'd wager the developers are poorly incentivized - if something satisfies the ticket and looks fast enough on their tiny test bed with state of the art hardware and great network (perhaps even localhost), it ships. On the other hand I don't consider something good enough to ship until it is fast enough on testbeds orders of magnitude larger than what I expect a "normal" workflow would include. The process of going from "works" to "fast enough for people using 100x larger inputs than I expect" almost never involves "rewrite in rust"[3], but instead "cache cleverly", "debounce discretely", and if all else fails "sit down and ponder on novel algorithms and data structures". These are all operations that are just as easy, if not easier, in high level languages as compared to rust.
[1]: Full disclosure I was paid to write vscode for a period. When VS Code is slow, and it 100% is at times, the root cause is almost always an extension blocking progress for some dumb reason. This absolutely blows, but isn't a problem rust would solve - indeed extensions can already invoke rust.
[2]: inb4 "but sublime!": Running experiments, I've found sublime to in fact be slower than a fresh VS Code install at working with very large files. Of course when you have extensions trying to do dumb stuff with the big files, VS Code can get worthlessly slow. Again not a problem rust would solve. Try it: make a 5M line file, click it open it in Sublime, then in VS Code. On my machine VS Code opens it well before Sublime can.
[3]: Yes there are some times when rewriting in rust is appropriate, for instance VS Code's search is ripgrep - but rust isn't handling the UI at all, it's running in a separate thread doing what it does best (multithreaded systems programming), while the main renderer thread is doing what it does best (rendering). This is the way forward for the truly "inner loop" code, IMO.
I already did. Like I said, that happens when there's zero network activity (the apps are a bit sluggish even with wifi deactivated) but also zero disk activity as well (according to Apple's tools), as caches are warm.
Sure, network in the apps I mentioned is also incredibly slow (mostly because of lots of requests and redundant data), but the real slow part is the interface itself. Dragging anything, typing text, clicking buttons, popups. Everything takes more time than a native app from 15-20 years ago.
Devtools show it's death by a thousand cuts. Thousands of sub-milisecond javascript functions, thousands of unnecessary re-renders. That happens in almost every operation. Even popup menus take a long time to show up, despite not really doing anything before such as loading data. This is in both Teams and Jira, btw, Slack is not as bad. Interestingly, Teams works faster when opened inside Safari rather than in Electron, but not by much.
> Leaping to "the stack is bad it needs to be rewritten in rust" is extreme
Thankfully I said nothing of the sort... You mention Rust a couple times more, so I guess this is something of a pet peeve to you, which I'll ignore since it has nothing to with my message. I was only answering to your query, I'm not interested in making arguments because I have no dog in this race. Like I said, other apps (even those written in the web platform) are faster than the examples I gave.
> I'd wager the developers are poorly incentivized - if something satisfies the ticket and looks fast enough on their tiny test bed with state of the art hardware and great network (perhaps even localhost), it ships.
In this case it's also not about being fast on localhost or having 100x more data, although this is definitely a safe bet on most products. It's slow even on the base case, even without doing anything remote, or having almost no data.
If you want me to wager on why this happens: developers work in a way they can retain their sanity. If there's weekly changes of scope, they'll program defensively in a way that allow quick changes. So there's no room for macro performance optimisations. The architecture is optimized for change, not performance. I know there are weekly stupid changes in Jira/Teams because I see bugs and little test features coming and going every single fucking week, frequently disrupting my workflow. VSCode on the other hand is a developer tool, and performance and familiarity seems to take precedence over quick stupid features. Why? VSCode is a dev tool, so developers indeed know better. In normal products developers are unable to fight back the asshole product manager or product owner changing their mind every other week.
My claim is that moving to Rust is useless without solving the unnecessary rerenders, and once those are solved moving to Rust would be pointless. So that only real problem is fixing bad coding practices, which has nothing to do with the underlying language. In fact using a lower level language is likely to make that much more difficult.
I actually agree with you.
I guess I’m kinda tired of people trying to lure me into arguments I don’t want to have.
Also love the work you do and enjoy following it. Your treatises on Oklab are the #1 place I send programmers of all skill levels to understand color.
The dismissive complaint was not without merit, but wow did it assume that the commenter's use cases are all that matter and all that need be considered. The world of UIs is much, much, much bigger than that, and awash in unsolved or badly-solved problems that matter a lot to many people.
I agree with the complaint, fwiw, when applied to an important subset of the design space. I even think it's useful to try to understand where the limits of that subset are, and why.
But saying that exploration is pointless because we're all happy living on this here big island and there's nowhere else that could possibly be better so why bother looking, it's all good except we still don't know why people keep dropping dead, but that's an acceptable drawback to what is otherwise a paradise on Earth—hang on a sec while I scrape off these leeches, they're so silly sometimes—and the people who think otherwise are just malcontents who ought to be out catching fish for the rest of us to enjoy.
Why exactly? What are the current state of the art UI toolkits leaving on the table performance-wise?