Это не сценарий использования HashMaps
в Rust. HashMap
имеет ключи, которые неизвестны во время компиляции, а это не то, что вам нужно. Структуры имеют фиксированный набор известных ключей, а перечисления имеют фиксированный набор возможностей.
Прямой перевод вашего кода создает структуру и перечисление, используя Option
для обозначения необязательных полей:
#[derive(Debug)]
struct Example {
value1: String,
optional: Option<i32>,
value3: Option<Value3Values>,
}
#[derive(Debug)]
enum Value3Values {
Hello,
Goodbye,
}
Хотя исправно, это может раздражать , чтобы построить по сравнению с тем, к чему вы привыкли:
Example {
value1: String::from("hello"),
optional: None,
value3: Some(Value3Values::Goodbye),
}
Мы можем применить некоторые шаги, чтобы улучшить его. Использование Default
плюс синтаксис буквального обновления struct упрощает все значения по умолчанию:
#[derive(Debug, Default)]
struct Example { /* ... */ }
Example {
value1: String::from("hello"),
value3: Some(Value3Values::Goodbye),
..Example::default()
}
Вы также можете применить черты Into
/ From
, чтобы удалить некоторые преобразования:
Example {
value1: "hello".into(),
value3: Value3Values::Goodbye.into(),
..Example::default()
}
И вы можете заключить его в макрос, чтобы избежать повторения:
macro_rules! thing {
($t:ident, { $( $name:ident : $val:expr ),*, }) => (
$t {
$( $name: Into::into($val) ),*,
.. $t::default()
}
);
}
fn main() {
thing!(Example, {
value1: "hello",
});
thing!(Example, {
value1: "hello",
optional: 32,
});
thing!(Example, {
value1: "hello",
value3: Value3Values::Hello,
});
thing!(Example, {
value1: "hello",
optional: 32,
value3: Value3Values::Hello,
});
}
Существуют даже способы использования строковых литералов для value3
, но я бы этого избегал. «Строго типизированные» API раздражают.
macro_rules! value3 {
("hello") => (Value3Values::Hello);
("goodbye") => (Value3Values::Goodbye);
}
fn main() {
thing!(Example, {
value1: "hello",
value3: value3!("hello"),
});
thing!(Example, {
value1: "hello",
optional: 32,
value3: value3!("goodbye"),
});
}
Могут даже быть хитрые трюки с макросами, чтобы избежать необходимости вызывать макрос value3!
внутри thing!
.
A очень продвинутый метод будет использовать скрипт сборки для генерации пользовательских макросов для каждого набора атрибутов CSS, возможно, с использованием MDN CSS JSON DB . Вы бы получили что-то вроде:
macro_rules! example {
( $( $name:ident : $val:tt ),*, ) => (
Example {
$( $name: example!(field @ $name : $val) ),*,
.. Example::default()
}
);
// Internal details
( field @ value1 : $val:expr ) => (Into::into($val));
( field @ optional : $val:expr ) => (Into::into($val));
( field @ value3 : hello ) => (Some(Value3Values::Hello));
( field @ value3 : goodbye ) => (Some(Value3Values::Goodbye));
( field @ value3 : $val:expr ) => (
compile_error!(r#"value3 can only be "hello" or "goodbye""#)
);
}
fn main() {
example! {
value1: "name",
};
example! {
optional: 42,
};
example! {
value1: "name",
value3: hello,
};
example! {
value1: "name",
optional: 42,
value3: goodbye,
};
}