Fly you fools. This will be a nightmare to debug, introspect and reason about for a speed boost that you (and your users) won’t be able to measure.
If you want to build a native app, more power to you. There are simpler languages that will enable you to do that with a much higher productivity.
Kudos to the library writer though!
IMO every end-user-facing interface should be based on async calls, which provides better composability and forces the developer to think the relations between all full possible interactions and not just the current call. Too many GUIs do weird things when the user clicks on several controls in quick succession before allowing the previous one to finish. The program should have a model for how to resolve such anomalous inputs, instead of leaving those interactions as undefined behavior or handling it as error-prone edge cases. Having an async framework isn't enough for that, but at least forces the developer to think about out-of-order interactions between commands.
If that makes reasoning about the complex it's because current debug & introspect tools are inadequate for async-heavy flows; but that's a reason to improve the tools, not to drop the flows. Better declarative languages and inspect tools would ease development in that style.
You can do the same with goroutines/green threads/virtual threads, without putting the burden of differentiation between sync and async functions on the programmer.
The only argument for async/await syntax I've ever seen is either "it allows traditionally sync languages to use async" (compatibility) or "it gives the compiler more information so it can make stuff faster" (speed boost).
I think the parent comment meant using Rust rather than a garbage collected language like C# or even Java for a GUI. Not just using async within Rust.
There is a reason most people still refer to Rust as a "systems" language.
It's time for us to take the institution back from the lunatics.
P.S. My website is new, so if you find any readability or accessibility issue, please let me know!
Best of luck and I'll be checking out where this ends up going.
One thing to not neglect is accessibility. Even if it's not fully baked yet giving it some thought in the API and implementing for web would be a big plus. Accessibility is the hill most non-mainstream UI toolkits die on. They usually leave it for later and then find that it's hard because they didn't think about it.
Being retained mode puts you ahead in the accessibility game right away.
The login form example which returns values reminds me a lot of imgui and other immediate mode GUI frameworks.
I´d expect an async UI to be in "immediate UI" style, e.g.:
async fn give_em_cookies(window:Win) {
win.title("Cookies!");
win.columnwise();
win.label("Which cookie do you like?");
let cookie = win.selector(&["Shortbread", "Chocolate chip"]).await;
win.label("When to you want to eat the cookie?");
win.rowwise();
if win.button("Now!").await { eat_cookie_now(cookie); }
if win.button("Later!").await { eat_coookie_later(cookie); }
if win.closed().await { no_cookie_eaten(); }
}
// the function represents the state machine that encodes
// the behaviour of the dialog box, which the caller gives to UI
// engine for rendering
ui.display(give_em_cookies(Window::new())).await;Concretely the login_form function works by racing a render (a true, never-completing component) with a "listener" future that completes when the user submits their login. Once the listener completes, the race is over and the render future gets dropped. We can then return from login_form.
It would also be great to see example code for that - it's the most significant part where I was looking for more information but couldn't find it.
Edit: A bit unrelated, but the tokio vs async-std split continues!
Edit 2: How does error handling work? Is check_login a component or regular request?
I'm not sure the complexity of doing everything using async constructs is worth it, though. Large-scale UI's built in Qt or Javascript are mostly single threaded anyway, but it's still worthwhile to explore so kudos for that. Looking forward to seeing how far you get.
This is literally the exact style of SwiftUI and Jetpack Compose (down to the author having used the term fragment, I sure hope this isn't leftover trauma from being an Android developer), except written in Rust (hence having to deal with lifetimes in the middle, default parameters, lambdas being quite verbose and needing to move things, etc).
Not blocking the UI thread is mandatory if you ever want to make any kind of complex UI. If you're a web dev, well you only have one thread anyways, good luck, if you're on any other platform, interactions _cannot_ ever block the UI (unless you, yourself, update the UI to say it is blocked). Making this async is a good thing.
Stack traces are a problem, but then again they've been a problem in any remotely capable UI toolkit.
With ReactiveCell, it looks surprisingly similar to what Compose does, where modifying a State<T> causes recomposition of everything observing it. Which means that it might be powerful enough one day to do the same things as Molecule (https://github.com/cashapp/molecule), or ComposePPT (https://github.com/fgiris/composePPT), where everything is a potential target and it interops really well with existing toolkits.
One thing I like is that the code appears to flow more like a console app than a GUI app. I've always found it's easy to create a quick-and-dirty console app; but if I want to do a quick-and-dirty UI (windows) app, it's much more time consuming.
This is because, with console IO, you can write your UI in a very linear manner. With UI (windows), it's much harder to write the code in a linear manner.
https://github.com/wishawa/async_ui/blob/main/examples/web-t...
[1] O(log N) for “our” code. I don’t know what the browser’s rendering/layouting engine is doing.
In general Async UI will still be noisier than SwiftUI or React, but I hope only in the way that Rust is more explicit/verbose than other languages.
how did we all survive before async UI... ¬‿¬
I don't understand the motivation about lifetimes in sync rust not being able to be arbitrary. I'm also confused because most of the time went you want to send data around in a async context you wrap it in `arc`, which has the pretty much analogous `rc` in a sync context which would also solve the lifetimes issue. Is there something I am missing?
class T {
public:
T(int & m): member{m} {}
int & member;
}
T MakeT(int m) {
return T(m);
}
int main() {
const auto t = MakeT(10);
std::cout << t.member << std::endl; // UB <- member has actually been destroyed
}
Rust will correctly observe that the lifetime of m in `MakeT` is lower than the T object returned and will refuse to compilePersonally I have mixed feelings on it. ObjC/AppKit was clearly a step up from classic object UI toolkits built in rigid languages like C++, but I find React and its immediate-mode brethren far more enjoyable to work with precisely because there is no amorphous graph of actors sending messages to each other.
The cool thing is this: with some effort, it is probably possible to adapt the two library together to have a UI with RUI's rendering system and Async UI's APIs.
But can you share more on what you need Tokio for? I'd completely understand if we're working with servers. But when it come to clients - UI applications - I feel like async-std and smol are pretty competitive.
Because everything. uses. Tokio. It's become the defacto standard of async Rust, whether people like it or not.
This issue has been notable enough to cause some projects to outright consider dropping async-std support - and I think they only relented because there's apparently enough (private, not-open-source) users who apparently still use it.
(https://github.com/launchbadge/sqlx/issues/1669)
(Note that they also have a note about async-compat not exactly being an ideal solution and that they'd be looking into writing their own)
But I think the distinction you made about "client-side Rust" not being as obviously-tokio-dominated as backend-side does make me stop and think.
Btw, this looks interesting regardless! Great job! Can I ask how your Rust learning journey went? I assume one needs an under-the-hood understanding of async Rust to build a library like this, and I'd love to hear about your learning journey getting to that understanding.
Still large, but it's because we're shipping a lot of code that we don't need. Mostly the APIs exposed by web_sys. Proper dead code elimination would bring it down a lot.
If we really want a lot of non-javascript langs to succeed, I think we need to focus on raw wasm, .wat is pretty good for teaching. But they have new thing coming up which is called "Component Model", something about interface types related, which may help reducing bloat binaries.
I feel like it is pretty close even though you don't see the async exposed? L
Sycamore:
> Write code that feels natural. Everything is built on reactive primitives without a cumbersome virtual DOM.
Yew works more similar to React