strum is a crate in Rust which makes working with enums and strings easier. In this blog we can’t cover every single thing that strum can do, but rather just focus on the main features.
Installation is nice and simple, we can just add
strum = { version = "0.26", features = ["derive"] }
strum_macros = "0.26"
To the dependency section of our Cargo.toml
.
Let’s start with what I think is the most basic implementation of strum
by making an enum
an EnumString
. You’ll notice throughout this blog that pretty much everything to do with strum
uses some kind of macro, which makes it nice and concise.
1use std::str::FromStr;
2
3use strum_macros::EnumString;
4
5fn main() {
6 let colour = Colour::from_str("Red");
7 println!("Colour: {:?}", colour);
8
9 let no_colour = Colour::from_str("No a colour");
10 println!("Not colour: {:?}", no_colour);
11}
12
13#[derive(Debug, PartialEq, EnumString)]
14enum Colour {
15 Red,
16 Green,
17 Blue,
18 Yellow,
19 Black,
20}
Notice how by deriving EnumString
on the Colour
enum
we automatically get the from_str
method. from_str
returns either the enum
variant or an Error
- usually something to do with the enum variant not being found. If we run this program we’ll find
Colour: Ok(Red)
Not colour: Err(VariantNotFound)
We can customise some of this behaviour with the following properties
1use std::str::FromStr;
2
3use strum_macros::EnumString;
4
5fn main() {
6 let red = Colour::from_str("r");
7 println!("Red: {:?}", red);
8
9 let not_green = Colour::from_str("Green");
10 println!("Not green: {:?}", not_green);
11
12 let blue = Colour::from_str("bLuE");
13 println!("Blue: {:?}", blue);
14}
15
16#[derive(Debug, PartialEq, EnumString)]
17enum Colour {
18 #[strum(serialize = "red", serialize = "r")]
19 Red,
20
21 #[strum(disabled)]
22 Green,
23
24 #[strum(ascii_case_insensitive)]
25 Blue,
26
27 Yellow,
28
29 Black,
30}
Using #[strum(serialize = "something")]
we can customise the strings that the variant will be deserialised from. Using #[strum(disabled)]
we can disable that variant for strum
- it won’t deserialize it. We can also use #[strum(ascii_case_insensitive)]
to make it so that any variation on upper and lower case spelling is accepted.
The next logical thing to to is to convert the enum
into a string. We can use Display
for that.
1use strum_macros::Display;
2
3fn main() {
4 let red = Colour::Red;
5
6 println!("Colour: {}", red);
7}
8
9#[derive(Debug, PartialEq, Display)]
10enum Colour {
11 Red,
12 Green,
13 Blue,
14 Yellow,
15 Black,
16}
This program will print
Colour: Red
when run. Obviously this is very easy.
We can customise how this is displayed though.
1use strum_macros::Display;
2
3fn main() {
4 let red = Colour::Red;
5
6 println!("Colour: {}", red);
7}
8
9#[derive(Debug, PartialEq, Display)]
10enum Colour {
11 #[strum(serialize = "red colour")]
12 Red,
13 Green,
14 Blue,
15 Yellow,
16 Black,
17}
This program will now print Colour: red colour
when run.
A slightly more efficient program would actually use AsRefStr
, which creates a &'a str
.
1 use strum_macros::AsRefStr;
2
3fn main() {
4 let red = Colour::Red;
5
6 let word = red.as_ref();
7
8 drop(red);
9
10 println!("{}", word);
11}
12
13#[derive(Debug, PartialEq, AsRefStr)]
14enum Colour {
15 #[strum(serialize = "red colour")]
16 Red,
17 Green,
18 Blue,
19 Yellow,
20 Black,
21}
Now of course the above program won’t compile because the lifetime of word is the same lifetime of the variable red
. What else can we do? We can also add a global prefix
1use strum_macros::AsRefStr;
2
3fn main() {
4 let red = Colour::Red;
5
6 println!("Colour: {}", red.as_ref());
7}
8
9#[derive(Debug, PartialEq, AsRefStr)]
10#[strum(prefix = "/")]
11enum Colour {
12 #[strum(serialize = "red colour")]
13 Red,
14 Green,
15 Blue,
16 Yellow,
17 Black,
18}
19
Which would print Colour: /red colour
.
The final thing we’ll look at today is adding some custom messages to our enum
. We can do this by annotating the enum
with EnumMessage
from strum_macros
, and we also need to import strum::EnumMessage
to use them
1use std::str::FromStr;
2use strum::EnumMessage;
3use strum_macros::{Display, EnumMessage, EnumString};
4
5fn main() {
6 let colour = Colour::from_str("Red").unwrap();
7 println!("Colour: {}", colour);
8 println!("Message: {}", colour.get_message().unwrap());
9 println!("Details: {}", colour.get_detailed_message().unwrap());
10}
11
12#[derive(Debug, PartialEq, EnumString, Display, EnumMessage)]
13enum Colour {
14 #[strum(
15 message = "A primary colour",
16 detailed_message = "Red traditionally represents danger"
17 )]
18 Red,
19 Green,
20 Blue,
21 Yellow,
22 Black,
23}
24
This program prints
Colour: Red
Message: A primary colour
Details: Red traditionally represents danger
when run.
That’s all we’ll cover today! I think those are the most useful things that strum
has to offer and will cover most use cases. strum
makes working with enum
in Rust that needs to be a string too significantly easier.