Why is building a UI in Rust so hard?
warp.dev
warp.dev
https://docs.google.com/presentation/d/1P8Su5mZSYkfZ1A9mPAaK...
This is because simply building the UI toolkit itself is usually not a prime motivator for a startup, they want to work on their differentiating features, not on reinventing the wheel. Of course, if the startup is a text editor or other such UI heavy app, then it may make sense to reinvent the UI wheel. Figma showed this successfully, and perhaps Zed and Warp might, too.
Compare this to my experience with iced, where I discovered that I love the elm architecture, but dealing with the UI parts became exponentially more complex the larger it grew, so many undocumented or under documented components, plus upgrading between minor versions would cause breaking changes that weren’t always easy to solve because of the lack of documentation or examples.
"Component Software: Beyond Object-Oriented Programming"
https://www.amazon.de/-/en/Clemens-Szyperski/dp/0201745720
"Authoring ActiveX with VB 6"
https://learn.microsoft.com/en-us/previous-versions/visualst...
"Traits - Composable Units of Behavior"
https://scg.unibe.ch/research/traits
"Working with Protocols"
https://developer.apple.com/library/archive/documentation/Co...
They exist now in Squeak and Pharo, both originally based on Xerox's Smalltalk-80 image.
ECOOP 2009 proceedings paper from a certain Simon Peyton Jones.
"Classes, Jim, But Not as We Know Them — Type Classes in Haskell: What, Why, and Whither"
https://link.springer.com/chapter/10.1007/978-3-642-03013-0_...
Container. Component. Context. Graph. I would think a "wonderful language" such as Rust should have no problem with such a basic conceptual setup, but apparently not. I personally am solidly in the adaptive components camp, specially for things like UI.
If you know Rust (I don't), curious if the monadic approach could work, propagating the state as a parameter.
But while reading the article, I was thinking "You're modelling the tree wrong! The memory layout doesn't need to know that it is a tree!" - gladly we came to the same conclusion.
The funny thing with this pattern is that Rust is much more loose when it comes to modelling with hashmaps and vectors. You are essentially creating user-space references and maintaining them with your own logic.
You're basically circumventing the borrow checker, but also a lot of other problems as you encapsulate the maintenance of these references within your tree / graph data structure.
But in a sense it's cheating! When you give me a key to a hashmap, do _I_ know whether the thing is there?
My guess is that you're adrift in a continuum made by somebody else who started with one goal, but found an ancillary surprise benefit worthy of persisting under the umbrella of the previous illusion. Bricks kept getting put down, the pamphlet never changed, then bam there you are.
I wager you're right in the mix before former turned to latter.
ChatGPT: I'm not a rust developer, but it seems like you know what you're talking about when it comes to detecting problems and traps.
My guess is that you're stuck in a situation that someone else created. They started with one goal, but found another good thing that they wanted to keep doing. They kept building and never changed their plan, and now you're stuck in the middle of it.
I think you were there before things started to get weird.
But the cool thing with laying out your graph like that, is that it has a relational character to it.
You’re essentially dealing in tables and (one/many) to many relationships.
You have a birds eye view of where your entities are allocated and how they are connected.
You still need policies around how you clean them up (remove an entity) and how the edges are affected. But you can reason about that in a similar way as you would in a relational database.
Once you start cheating you might as well write yourself a garbage collected language and use that to write your UI library.
This is what the borrow checker does. The index is just for doing an If let Some(entry) = map.get(&ident) {}
and not fail if it does not exist. Do you want to keep stuff alive even if they are removed? You can use reference counter wrappers (Rc and Arc) just fine.
The right pattern for graphs etc in vectors and maps is that the owning struct/type is responsible for keeping its internal state (indices it knows to exist) consistent. But that's not a memory safety issue, it's a logic issue
How about "Every GUI toolkit out there trying to avoid using web tech is going to end up implementing a half-baked implementation of CSS", to coin a phrase.
The problem is it's not guarantee, and it's way more expensive and hard.
Hence electron being so popular.
Additionally, UI work on the web is pretty much only CSS. React doesn't handle drawing elements, it delegates it to the browser. React is the easy part of drawing UIs. You could also make a fancy DSL in Rust for Qt and say "look how bad the web is my UI runs 50 times faster clearly Rust is better at rendering UIs", but we both know that's a lie.
Also, CSS is an awful wart and alternatives that have popped up like Compose's Modifier system and CompositionLocals are infinitely better.
Ancient knowledge lost to the druids in the mountains and dead trees instead of TikTok tutorials.
This mage will always pick native over Web when given the option, even though Web has paid the bills most of the time since the last 25 years.
I get them, but I'm just not interested anymore with 3 desktop and 2 mobile platforms to think about. Sure it makes sense sometimes, but this should be fun.
"You want a button? you have to style a div to get what you want". "You want to center an element vertically? You have to use a table. You have to use these css hacks." "You want to debug the component you've written? Enjoy diving through the div soup".
Of course, there's nothing stopping us from being able to use just a Canvas/WebGL/etc.. based frameworks. Hell even the imgui, qt etc.. have webassembly/webgl targets. But so far, they seem to do badly when it comes to content addressability, accessibility and search engine optimization.
The only good things I have to say about electron: They have decent free tooling that makes producing cross platform binaries easy/easier than the native alternatives. Accessing http resources in javascript is nicer than trying to do the same in C++/Java/etc... The latter is a very important consideration in this day and age.
We try, we can, and we do. Successfully.
> "You want a button? you have to style a div to get what you want". "You want to center an element vertically? You have to use a table. You have to use these css hacks." "You want to debug the component you've written? Enjoy diving through the div soup".
Honestly I don’t think you’re entitled to an opinion on this given you obviously haven’t done anything web related in the last 20 years. I mean, even <button> was there from the beginning. Flexbox layout itself is almost 10 years old now. CSS hacks are mostly unheard of since the death of Internet Explorer 9.
My point is not that you can't do it. It is about what kind of abstractions people have hacked past to beat a document into behaving like an application.
That's why I think every couple of years people keep reinventing the wheel to accomplish the same basic things. JQuery-Ui, Sencha, Angular, React, Vue, Svelte etc... Inline styling was a sin. Then it's the recommended practise. Not even sure what's hip these days in what framework.There is a reason why your actual application abstractions get lost when you inspect an element in developer tools.
I'm well aware of button tags and i am also aware of the reasons why people styled div and anchor elements to behave like buttons in the codebases I've dealt with. They had good reasons why they did those things the way they did.
If you reread my above comment, You'd see how I've also mentioned the tradeoffs of actually using proper ui/application development libraries and frameworks to make "Web Applications".
But if one is making an electron application, search engine discoverability and content addressability bring absolutely nothing to the table and one might as well use a more appropriate framework.
No, you don't, there's a <button> tag. If you don't like the default button style, then yes, you'll need to style it. But that goes for any UI framework.
In my experience the key to solving accessibility etc, when building out a canvas-based web UI, is to work with the DOM instead of trying to replace it. Use <input> to control the placement and display of graphical elements, <button> elements to start/stop animations, etc; make good use of DOM events to track user movement/interaction across the canvas; tie clickable actions to real <a> elements instead of trying to reinvent them. Graphical text also needs to be reflected back into the DOM. Good use of ARIA also helps. Etc. And don't be afraid to tap into CSS --variables or even HTML data- attributes: not everything needs to be defined in the JS (or WASM-enabled language of choice) canvas code if the end result - a <canvas> UI or infographic - is destined to live in a web page.
Proof-of-concept of an accessible design system: https://codepen.io/kaliedarik/project/editor/DVeMVj
Proof-of-concept of an accessible set of related graphs: https://codepen.io/kaliedarik/project/editor/AMVKPx
Then where is it?
Also, I can make a standalone app in a few hours using Electron. I guess it would take me more than a few weeks with Delphi, given that I don’t know anything about it. So your point might just be that you know Delphi and not JavaScript.
Of all people, tech workers and enthusiasts should know that being superior doesn't ensure survival.
Are there even Delphi enthusiasts still, or is it just nostalgia? Would the people that claim Delphi is superior use it?
There are.
There is a major commercially relevant DAW produced in Delphi. You can figure out which one.
> I believe Electron and web technologies are superior for UIs given their pragmatism and ubiquity.
This opinion is questionable given a clearly narrow view. Outside of this bubble they aren’t ubiquitous.
There are plenty of LOB apps still used and maintained in VB6. One of the most heavily used applications in the US federal government is a C++Builder app (the C++ RAD counterpart to Delphi) - it is a piece of crap, but that’s a factor of the authors not the language - and when it was first released more than 20 years ago it started up in a few seconds on hardware at the time with slow spinning disks! Looking at the vast majority of LOB web apps - nothing has gotten better on that front.
One of the employees they lured away was Anders Hejlsberg, former chief architect of Delphi, which built C# at Microsoft. WinForms had an uncanny resemblance to VCL.
Before that Microsoft had tried to extend-embrace-extinguish Java with J++, but got sued by Sun into submission.
MS were utter scum back then, but Borland’s mistakes certainly didn’t help either.
Of course, 99% of the time the server and client are ran together, so you don't notice it is distributed. But that doesn't mean it isn't.
I can understand the appeal and advantages of using native GUI tech. Really. But there is a reason people use stuff like Electron/Tauri/Wails. Web tech has seen so much investment, and as a result, you now basically have a app platform that runs anyhwere, offers a huge ecosystem and a great developer experience. Which I never found to be matched in any other GUI toolkit.
If there was a GUI toolkit that offered me all of this without requiring the bloat of a browser, then I would switch in an instant. It's just not there.
I never missed any GUI builder. Browser dev tools are phenomenal IMO, you can drag&drop elements across your layout, change any part of your styles and instantly see the effects, measure rendering performance, memory consumption, debug your Javascript or simulate different screen sizes.
About React, nobody forces you to use it. There is a myriad of frontend frameworks, at all levels of complexity. If you feel your app is so simple that you don't need any framework, you can do without. Frameworks (and again, there is a huge world outside React!) came up because they make managing state in complex UIs easier.
"In a few hours instead of weeks" I'd argue that the web stack only takes less time, not more.
JavaScript is popular because developers like it, because it works well, not because some "large corps" "push" for things to happen. There are more than scattered evidences to show that.
Next time, come up with something better and more convincing when hating the web stack.
For better or worse, but the reality is nobody wants them anymore. Branding is a thing. When a client wanted something different you used to tell them "You can't have that" and they had to take it, because there was no other option.
The instant web enabled and normalized arbitrary UIs, "You can't have that" Delphi people simply went out of business. Because it turned out Delphi was far inferior at doing what people actually wanted.
What people think they want and what would make them happier and productive in the long run are different things. They buy junk food and complain they don't feel well. They buy Marvel movies, but in the long run stop going to the theater because movies aren't interesting anymore. Modern web apps are basically the incarnation of that principle.
Are people really happier and more productive with shitty web apps? Because it seems like everyone complains about Slack, Teams, etc. Google Docs certainly doesn't have the die-hard fans Word Perfect still has. Apple still collects quite a premium remembering that people don't know what they want and need people with judgment to make decisions for them.
Were you actually there in the 90s and 00s or are you just imagining how things could have been?
I personally developed fully skinned UIs in Delphi’s cousin, C++ builder on Windows in the late 90s and in the 00s. In addition to that, VCL apps had a rich palette of drag & drop widgets, including commercial products that were better than anything the competition was offering, including Microsoft.
I used a lot of Windows apps over the years and literally everything from custom windows shapes, colors and background, to custom widgets and cursors, etc I’ve ever seen was possible to develop in Delphi or C++ builder.
”The instant web enabled and normalized arbitrary UIs, "You can't have that" Delphi people simply went out of business. Because it turned out Delphi was far inferior at doing what people actually wanted.”
Delphi was mismanaged by Borland, sabotaged by Microsoft and they charged a ton of money for the software. Starting with WinForms and C# MS finally had a reasonably competitive alternative to offer.
The web had nothing to do with Delphi’s decline. In the 00s we used a combination of back-end languages with some JS for enhancement and Flash for special cases or multimedia-heavy sites. Macromedia Flash remained the most capable web-based technology for a long time after Delphi was already in decline. As a former Flash developer, I can say confidently that Flash and Delphi/C++ Builder served different use-cases and were not competing with each-other.
Even MS used to provide that with frontpage.
The real answer to your question lies in why people don't use them much.
I would like to say the Web was poorly designed but that would be too generous. The Web wasn’t designed at all. It’s a hodgepodge of poorly thought out technologies hastily cobbled together. It’s a piece of technology designed to display static textual content horribly contorted to make applications.
It took decades for JS and CSS to reach a state I consider barely useable and the best we could do is apparently flexbox.
And grid.
The contortions you have to go through to get the equivalent of early 2000 Qt's spacer items and anchors...
So the VB/Winform model of building GUI on the web has to re-invent all controls from scratch, then all dat abinfding from scratch, then...
You can still do all that, on Windows. As I get older and have more experience, I have come to the conclusion that some people are more interested in the technology and less so in solving actual problems. While I'm pretty happy with my career, if I were to do it all over: Windows, Visual Studio, C++ and WinForms would have been the tools that I'd have picked. We're coming up on 25 years of a fairly stable, actively developed platform with pretty good backwards compatibility.
Also, I remember not being completely precise: I maintained a hacked together GUI build in Access VBA. It had its warts but its web-based replacement didn't blow it out of the water and took longer to develop.
It's smoke above pinched fingers creating a bottleneck. Whether the fingers belong to bureaucracy or dev capability requires more info than I have. My bias says look at whoever has the biggest ego and the strongest opinions. They're the anchor.
One of the primary issues is that there is no foundational cross-platform support. For creating windows, GLFW is the best bet. I should give kudos here. GLFW works and is well-documented, a rarity indeed. The forum is also pleasant. For graphics, Skia is probably the best solution, but it has problems. For example, it is 2D only, has support on a Google Group with not much traffic, and is complicated to build. The binding for it on .NET is currently not actively maintained, despite being effectively owned by Microsoft. You also give up performance. For example, Direct2D is much faster than Skia on Windows, and Direct2D can seamlessly interact with Direct3D in the same window. But SharpDX (the .NET bindings) is no longer maintained and DirectX is of course Windows only. Then you have macOS which is completely antagonistic. It is locked to an extremely old OpenGL version, and Apple may remove it at any indeterminate time. So you either build against OpenGL or start jumping through hoops in GLFW and Skia to use Vulkan and Molten to talk to Metal on macOS. Also, macOS is the only OS that forces the window management on the main thread. All window management on all OSs is on a single thread, but on Windows, for example, that thread can be any thread. Then there's Linux and the umpteen million distros and the two window managers in X11 and Wayland. There is no cross-platform accessibility library that I know of.
On top of all this, all pf these solutions have limited to no support. Most projects are ran by just a couple of people, if that. This is not a complaint against them in any way, as it is simply an observation of the reality in that support is highly, highly limited. There's also the issue that many of the issues encountered are complex and often reach across several stacks. For example, is it a bug in Skia, GLFW, the bindings to these, the OS, the graphics drivers, or from the interaction of these.
There are also very few resources and references for stuff like this.
Much faster? I’m not necessarily doubting this but you have some evidence? Isn’t Skia even used by Edge? And everybody here is basically claiming that the only UI frameworks that matter are web based - so if it’s good enough for that…
Also Qt Quick - seems to cover all the platforms mentioned. Of course it has a ton of resources poured into it, but it’s there.
In any case this article is talking about the features that are unique to Rust that make it especially hard to write GUIs.
Though weirdly it focuses on lack of inheritance which is definitely not that big of a deal (Rust does have trait inheritance - you can even get full implementation inheritance using macros if you really want but I doubt it is needed). The biggest issue by far is state management because of the single ownership rules.
You can of course Rc-RefCell everything but that gets very ugly.
My experience using ECS in bevy is it feels like fancy spaghetti code. I'm not convinced it's a great approach, at least on the small scale I'm working at.
I'm super interested in this stuff and would like to hear more.
OO is typically regarded as making it easier to extend the data types of a program, since you can introduce new ones by inheritance at any time. Whereas pure FP systems specify types exhaustively at definition.
FP typically makes it easier to extend the functions, since you can define arbitary more functions on the "public" type system; whereas OO encapsulates stuff and makes it harder to do that.
> You can build an extension point into the ADT for user defined components
I assume that means there are ways of making the pure-FP approach OO-like, and vice versa.
I think its nevertheless to speak about affordances here, and rust is in my view, one of the poorest DX/affordancey languages --- the syntax and semantics conspire to create lots of friction.
This friction isnt really considered in "the expression problem", but i think is very important in practice.
We should have a "frictionless expression problem" and I think that's the issue with rust: it doesnt frictionalessly extend in the ways needed by UI programming.
Searching for "expression problem" should find other discussion of the problem and solutions. The two best known solutions to the expression problem are:
- data types a la carte, which takes an FP style approach; and - tagless final or object algebras, which takes an OO style approach.
Other solutions are possible depending on language features. E.g. the linked paper above uses mixins and units.
Overall I find the discussion in the original post to be a bit naive. It doesn't make the case that an OO approach is necessary or desirable. E.g. that an OO approach provides a form of extensibility that is needed.
In the FP world, if I wanted to build a UI toolkit I would start by defining by a combinator library (usually implemented as an algebraic data type) that describes the UI and then have an interpreter that constructs everything on the screen. This hides all the state within the interpreter, which makes it easier to avoid issues with the borrow checker. The OP didn't discuss this approach either.
I don't think anyone's saying you can't do it without OO. Just that existing popular patterns for UI tend to rely on OO patterns like inheritance and classes with instances, and easy-ish namespacing and ownership concepts that map to those ideas.
So doing it without OO is harder, as it requires something different, that may be studied, but is less well explored and documented to the average person.
I recall similar discussions about doing UIs in C. GTK, for example, sort of fakes OO in C, I assume for related reasons.
React, Angular (esp. with ngrx), Svelte
Yew and Leptos don't fake oop either
Sure, though they rely on a lot of things that are only bundled with a web browser like the DOM and CSS and their concepts of namespacing, encapsulation features, etc.
Rust can provide encapsulation namespacing as well
UIs are inherently async and event drive, it's callbacks over callbacks, again, harder in rust.
UIs have a lot of detail. A little screen has a lot of hidden bit of small complexity.
Rust is a heavy language that wants to treat every line of code as an existential security threat and performance.
It's really not the right tool.
(Oh hey that’s a thing! https://www.fltk.org/articles.php?L1771 )
http://www.areweguiyet.com (seems updates to this thing have stopped?)
https://blog.logrocket.com/state-of-rust-gui-libraries/
The state of TUI on the other hand is better (posted recently, there are a few others):
Not only does this allow to use "normal" trees for setting up components, it also reuses most of the usability features.
Drawing a box and some text has been solved now. Several Rust libraries can render entire UIs with all the common controls you can think of. The downside of using these pure Rust libraries is that they seem focused more on features than usability. The created UI looks like it belongs inside a video game engine. It doesn't match any of the controls you'd expect to see, it doesn't even try to use your system's theme settings, or even the common OS conventions in some case, especially if you're trying to go cross platform.
One of the best ways to develop snappy GUIs that stick to your system's settings and themes is to use the windows crate and package Wine with your executable. Wine can emulate your system's theme quite convincingly and the Windows API solves most of the accessibility problems. It's a massive pain to develop against, but in my opinion the end result is much more convincing as a "real" UI than many of the native libraries.
After that, Qt provides some pretty good control libraries but interacting with it from Rust is a little painful as you need to choose between tons of wrappers or dropping Rust's safety mechanisms with unsafe{}.
Personally, I'm hoping wxRust gets worked out more, as its still in its early stages by its own admission m. WxWidgets solves my usability problem well enough and is lightweight enough that I think it could finally solve my UI usability quirks. Sure, you'll have the lack of safety guarantees and rely on the C renderer, but WxWidgets has been out for so long that I'm confident the types of bugs switching Rust would solve have mostly been found by now.
I believe, you are confusing performance with responsiveness. Where a traditional mutating UI framework would happily update a widget and keep CPU idle most of the time, your approach keeps wasting processor cycles to build widget tree on demand for rendering. This could be unacceptable for devices with an energy budget (IOT, for example). So, your approach works well where loss of efficiency is not critical.
Is there a "traditional" UI framework that does this? Most UI frameworks are retained anyways and will build a tree (ex. Qt, Fltk); and will render at 60fps (or throttle is there is nothing to do). As far as overhead goes, I can't imagine the Rust approach being anymore expensive.
The approach of just updating and rendering a single widget is hypothetically more performant, but I rarely see it done in the wild due to how complex it can become as your UI gets more complex.
IMHO, Rust's anti-C/C++ narrative also starts from wrong assumptions: in traditional Unix, what you're doing with C (apart from the OS) is implement the runtime of higher-level languages, but no-one has bothered to implement JavaScript or other engine with GC (let alone JIT) because committing to Rust's one-size-fits-all borrow checker just means the result can't be competitive. malloc()/free() also sucks, but its problems are only amplified by today's desire for single-process multithreaded or async backends; for one-shot commandline apps or process-per-request backends OTOH, there hasn't been much of a problem.
If you set out to write a new language in Rust, to escape the perceived limitations of the language, you end up extending Rust, not building a new language, and now you have Rust without the limitation you started with, and others can benefit from your work.
I've written two videos on the topic that I'd love to know your thoughts on, the first talking about Rust's universality due to both the unsafe and macro systems: https://www.youtube.com/watch?v=PuMXWc0xrK0&list=PLZaoyhMXgB...
The second a deeper dive into the macro system: https://www.youtube.com/watch?v=MWRPYBoCEaY&list=PLZaoyhMXgB...