Rustで文字列の定数を定義したくなることがある。 文字列定数の実現方法にはいくつかあるがmatch構文の恩恵を受けられるenumを使うと良い場合がある。 ところがRustのenumはC言語のそれに近く、enum variantの値として整数リテラルを設定することはできるが、文字列リテラルを設定することはできない。 正確にはenumのvariantはコンパイル時に全て整数値へ置き換えられる? 指定しなかった場合はC言語と同様に0から採番される。 次のようには書けるが、
// これはできる enum TCPFlag { FIN = 0x0001, SYN = 0x0002, RST = 0x0004, PSH = 0x0008, ACK = 0x0010, URG = 0x0020, }
次のようには書けない。
// これはできない enum ConfigKey { MaxMemory = "MaxMemory", MaxProcess = "MaxProcess", AllowEventuallyConsistency = "AllowEventuallyConsistency", }
これを解決するためには各enum variantと文字列の相互変換を可能にすればよい。
このとき、独自に相互変換のためのメソッドを定義するよりは、標準ライブラリのトレイトを実装した方が高い汎用性が得られる。
ようにenumから文字列への変換にはstd::fmt::Display
を、文字列からenumへの変換にはstd::str::FromStr
を実装すればよい。
設定値のkey-valueがどこかに保存されていると想定し、設定値のkeyをenumで定義するサンプルコードを次に示す。
なお、分かりやすさのためにこのコードではuse
を使用していない。
enum ConfigKey { MaxMemory, MaxProcess, AllowEventuallyConsistency, } impl std::fmt::Display for ConfigKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = match *self { Self::MaxMemory => "MaxMemory", Self::MaxProcess => "MaxProcess", Self::AllowEventuallyConsistency => "AllowEventuallyConsistency", // 安全のために_を使わない。下手に_で網羅するとバクの温床になる。 }; write!(f, "{}", s) } } impl std::str::FromStr for ConfigKey { type Err = &'static str; fn from_str(s: &str) -> Result<Self, Self::Err> { match s { "MaxMemory" => Ok(ConfigKey::MaxMemory), "MaxProcess" => Ok(ConfigKey::MaxProcess), "AllowEventuallyConsistency" => Ok(ConfigKey::AllowEventuallyConsistency), _ => Err("undefined configkey or lack of implimention"), } } } fn main() { // std::fmt::Displayトレイトの実装を呼び出し、enum variantから文字列へ変換する。 println!("{}", ConfigKey::AllowEventuallyConsistency); // std::str::FromStrトレイトの実装を呼び出し、文字列からenum variantを得る。 { // 有効な文字列を指定して変換を成功させる。 let valid = "MaxMemory"; let valid_ret = match valid.parse::<ConfigKey>() { Ok(_) => "valid key; ok", Err(_) => "invalid key; ng", }; println!("{} is {}", valid, valid_ret); } { // 無効な文字列を指定して変換を失敗させる。 let invalid = "foo"; let invalid_ret = match invalid.parse::<ConfigKey>() { Ok(_) => "valid key; ok", Err(_) => "invalid key; ng", }; println!("{} is {}", invalid, invalid_ret); } }
実行結果
AllowEventuallyConsistency MaxMemory is valid key; ok foo is invalid key; ng