Not gonna lie. I wrote my first programs 34 years ago but I never was a "real" developer in the sense that I'd write fast desktop apps, manage threads, and all that low level stuff. So learning Rust in the past few months, even if I have some very basic experience with programming in assembly, is still a lot to digest. However, today I got back to my test project and am really hyped that I have.... a button that increments a number.
"Ha, I can do that in javascript in 10 minutes." I mean yeah. Obviously. Anyone can. Here's the cool thing tho. I made mine overly complicated.
The UI looks as you'd expect it to, mostly a starter project leftovers:
The HTML is as simple as can be, just plain HTML and javascript, no compile step. We live in stone ages here and we love it.
The submit button has a simple handler in javascript:
This is, once again, trivial, and all just from the template project. Bottom part says "when a user clicks this button, call "greet" function". The top part is the greet function that invokes a Tauri command also called "greet".
What's Tauri? An open source project that lets you write JS/TS/Rust applications with WebView and bundle them as stand-alone, self-contained, one-file applications for desktop, and starting with Tauri 2.0 (now in beta.2) also for Android (and later iOS). If you know Electron (Slack, Spotify, Discord etc all use Electron, they're just websites with Chromium and C++ code packaged around them).
Anyway. Tauri runs a Rust "server" application that serves your HTML/JS app, but also lets you run high-performance Rust code. Adding a command is relatively simple:
Here's where things get interesting. For me.
Because I wanted to learn Bevy, a game engine written in Rust, because I want to learn how to write using a high-performance functional-programming-like pattern called ECS (Entity Component System), I have added Bevy to this project.
However, both Tauri and Bevy block on the main thread, so I had to find a tutorial on how to spawn Bevy in a different thread, and how to pass information to it. An example:
#[tauri::command] turns a normal function into a Tauri command that I can call from HTML/JS. It injects resource called BevyBridge which is just two lines of code:
#[derive(Resource)]
pub struct BevyBridge(pub Sender<u64>, pub Receiver<;u64>);
Sender and Receiver being from crossbeam-channel bevy crate which is for sending data back and forth safely and quickly between individual threads.
so "state.0.send(1)" means I'm sending a 64-bit unsigned integer with a value 1 to the channel.
And this is how to receive the message - inside of Bevy engine, in a separate thread. For simplicity, if I send zero, it resets the counter, and if I send any number it adds 100000 to the number, just for clarity. (Elsewhere I'm incrementing it by 1 on every game loop, so theoretically 60x a second. Or 15000x a second because Bevy is unreasonably fast and it doesn't need to render anything in this setup.)
And the best part is that with a single command (cargo tauri build) I get an .msi file, an .exe installer, both around 4MB, and a 11MB .exe file with no dependencies besides WebView (installed on every current desktop OS by default). There's just something about giving someone a floppy disk with an executable that you made yourself.
Is it dumb? Yes. Does it make me happy? No. Does it make me glad, and very relieved that I'm not completely lost? You bet.