Counter example part:
// (Remove the line below once any of your `Msg` variants doesn't implement `Copy`.)
#[derive(Copy, Clone)]
// `Msg` describes the different events you can modify state with.
enum Msg {
Increment,
}
pub enum Msg {
UrlChanged(subs::UrlChanged),
ScrollToTop,
ToggleGuideList,
HideGuideList,
ToggleMenu,
HideMenu,
SearchQueryChanged(String),
ToggleMode,
SwitchVersion(SeedVersion),
}
Msg
s are sent when something interesting has happened (e.g. the user has clicked a button) and your app should respond in some way (e.g. a value in your Model
should be increased).
You can also send Msg
by calling orders.send_msg(Msg::MyMessage)
Msg
has similar limitations like Model
- it can be almost anything, however the most Msg
s are enums in real-world apps. And Msg
has to be static
(it basically means that you can't send the most messages that contain references).
(We'll talk about sending and handling messages in next chapters.)
Msg
All Msg
variants should be as simple as possible and hold only data or ideally nothing.
enum Msg { MenuItemClicked(MenuItemId), Save }
If you can choose the type for your ids (e.g. MenuItemId
), pick Uuid rather than String or u32. Uuid
implements Copy and allows you to create entities in your frontend app before they are sent to your server.
Don't make your life unnecessarily hard.
Msg
generic.Msg
methods.Try to be as expressive as possible - reduce the number of simple types (bool
, String
, u32
, Option
, etc.) to a minimum. Even type aliases are a huge improvement for readability.
There are basically two types of messages. Try to follow the naming conventions:
ScrollToTop
, ToggleMenu
, RemoveItem(ItemId)
, etc.ButtonClicked
, UrlChanged(subs::UrlChanged)
, TextUpdated(String)
, etc.derive
#[derive(Copy, Clone)]
We already know about the allow
attribute from the previous chapters.
The most used attribute, however, is likely derive
. I recommend reading the official documentation, particularly these parts:
Notes from the front line:
Derive Copy
and Clone
for all items, where possible. It will make your life easier, and both Clippy and your users will appreciate it.
derive
values are order-sensitive because they are sequentially applied to the code below them. That's why the alphabetical order in derive(..)
is not important and by convention Copy
should be always before Clone
for better code scannability.There are edge-cases where you derive Clone
without any compilation issues, but you have still problems with calling my_item.clone()
. In the most cases, you can resolve this by implementing Clone
manually. (Related Rust issue)
Derive Debug
at for all public items, if possible. Users will be able to fix the most of the bugs in their apps, and you'll receive more meaningful bug reports. Seed has a log!(item_a, item_b);
macro that you can use instead of println!
to log things that implement Debug
directly to the browser console.
Only derive Default
when you are sure it's really useful. If you decide to implement Default
manually, make it as simple as possible.
Eq
and PartialEq
are often useful for simple enums. They allow you to use the ==
operator, as opposed to things like pattern matching and the matches! macro, which are less readable. Example:
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Visibility {
Visible,
Hidden,
}
...
IF!(model.menu_visibility == Hidden => C.hidden)
// VS
IF!(matches!(model.menu_visibility, Hidden) => C.hidden)
// or
if let Hidden = model.menu_visibility { Some(C.hidden) } else { None }
(You'll learn more about Seed macro IF!
in next chapters; C.
is a typed CSS class container used in seed-quickstart-webpack.)