Hot Reload Internals
Hot reload allows you to modify your Rust code and see changes instantly in the running application without restarting the app or losing state. WaterUI achieves this by compiling your code into a dynamic library (.dylib, .so, or .dll) and injecting it into the running process.
Architecture
The system consists of three parts:
- The CLI (
waterui-cli): Watches your source files, rebuilds the project as a dynamic library when changes are detected, and hosts a WebSocket server. - The Runtime (
HotReloadView): A special view component in your app that connects to the CLI, downloads the new library, and swaps the view pointer. - The Macro (
#[hot_reload]): An attribute macro that instruments functions to be individually reloadable.
Per-Function Hot Reload
You can mark specific view functions with #[hot_reload]. This wraps the function body in a HotReloadView.
#![allow(unused)]
fn main() {
#[hot_reload]
fn my_feature() -> impl View {
vstack((
text("Edit me!"),
button("Click me", || println!("Clicked")),
))
}
}
The macro generates a unique C-exported symbol for this function (e.g., waterui_hot_reload_my_feature).
The Reload Process
- Change Detection: The CLI detects a file save.
- Rebuild: It runs
cargo buildwith thewaterui_hot_reload_libconfiguration. - Broadcast: The new binary is broadcast over WebSocket to the running app.
- Load: The app writes the binary to a temporary file and loads it using
libloading. - Swap: The
HotReloadViewlooks up its specific symbol in the new library. If found, it calls the function to get the new view structure and replaces its current content.
State Preservation
Because only the view construction logic is reloaded, the underlying state (held in Bindings or the Environment) is preserved. The new view structure simply re-binds to the existing state.
Limitations
- Symbol Stability: The function signature must return
impl View. - Global State: Changes to global state initialization or
mainentry points usually require a full restart. - Struct Layout: Changing the fields of a struct that is shared between the main app and the hot-reloaded library can cause undefined behavior due to ABI mismatches. It is safest to tweak view logic and local variables.