This example demonstrates the minimum requirements for using the #[hot_ice::hot_fn]
macro on any type that returns messages. Unlike the hot_state example,
this example shows what you need to add when using hot_fn without hot_state for persistence.
When using #[hot_ice::hot_fn] on functions that return messages
(like update functions), all functions that return messages must use the macro.
This is the fundamental requirement for hot reloading to work correctly.
In ui/src/lib.rs, notice which functions have the macro:
impl State {
#[hot_ice::hot_fn] // ✅ Required - returns Task<Message>
pub fn boot() -> (State, Task<Message>) { /* ... */ }
#[hot_ice::hot_fn] // ✅ Required - returns Task<Message>
pub fn update(&mut self, message: Message) -> Task<Message> { /* ... */ }
#[hot_ice::hot_fn] // ✅ Required - returns Element<Message> (can produce messages)
pub fn view(&self) -> Element<'_, Message> { /* ... */ }
#[hot_ice::hot_fn] // ✅ Required - returns Subscription<Message>
pub fn subscription(&self) -> Subscription<Message> { /* ... */ }
// ❌ NOT required - returns Option<Theme> (not a message type)
pub fn theme(&self) -> Option<Theme> { /* ... */ }
// ❌ NOT required - returns theme::Style (not a message type)
pub fn style(&self, theme: &Theme) -> theme::Style { /* ... */ }
// ❌ NOT required - returns f32 (not a message type)
pub fn scale_factor(&self) -> f32 { /* ... */ }
// ❌ NOT required - returns String (not a message type)
pub fn title(&self) -> String { /* ... */ }
}Since we're not using #[hot_ice::hot_state], the state doesn't need
serialization traits:
#[derive(Debug, Clone)] // ❌ No Serialize, Deserialize, Default needed
pub struct State {
counter: counter::State,
todo_list: todo_list::State,
settings: settings::State,
}Component states are also plain structs without serialization requirements:
#[derive(Debug, Clone)] // ❌ No serde needed
pub struct State {
value: i32,
auto_increment: bool,
}Functions that return message types MUST use #[hot_ice::hot_fn]:
boot() -> (State, Task<Message>)✅update(&mut self, Message) -> Task<Message>✅view(&self) -> Element<Message>✅subscription(&self) -> Subscription<Message>✅
Functions that don't return message types DON'T need the macro:
theme(&self) -> Option<Theme>❌style(&self, &Theme) -> Style❌scale_factor(&self) -> f32❌title(&self) -> String❌- Any other helper functions returning non-message types ❌
| Feature | hot_state | hot_message |
|---|---|---|
| Make changes to state | ✅ Yes | x No |
| State persistence | ✅ Automatic | ✅ |
| Serialization required | ✅ Yes | ❌ No |
#[hot_ice::hot_state] needed |
✅ Yes | ❌ No |
| All functions that take &self need macro | ✅ Yes | x No |
| All message functions need macro | ✅ Yes | ✅ Yes |
| Simpler setup | ❌ | ✅ |
This example contains the same three modules as other examples:
- Simple counter with increment/decrement
- Returns
Task<Message>from update - No serialization required
- Add, toggle, delete todos
- State persists during runtime but not across hot reloads
- No serialization required
- Theme selection and scale factor
- Real-time updates
- No serialization required
cargo run --release- Run the application:
cargo run --release - Make changes: Modify any function that returns messages
- Hot reload: Application automatically reloads with changes
- State behavior:
- State is preserved across hot reloads
- State type can not be changed without complete recompile
- UI changes are preserved
Use #[hot_ice::hot_fn] without hot_state when:
- Rapid prototyping: You want the simplest hot reload setup
- Unsupported: If you are unable to implement traits
- Performance focus: Avoid serialization overhead
Use #[hot_ice::hot_state] when:
- State persistence: You need to maintain state across hot reloads
- Complex applications: State management is critical
- User experience: Losing state would disrupt workflow
- All message functions must use the macro - Missing any will break hot reloading
- State type changes must be recompiled - Changes to the state type require a complete recompile of the application.
- No serialization overhead - Faster compilation without serde requirements
- Simpler setup - Less boilerplate code compared to hot_state