Вопрос или проблема
В настоящее время я реализую трейты Display
для перечислений следующим образом:
#[derive(Debug)]
enum Thing {
SomeThing,
AnotherThing,
}
impl Display for Thing {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Thing::SomeThing => write!(f, "SOME_THING"),
Thing::AnotherThing => write!(f, "ANOTHER_THING"),
}
}
}
Это кажется утомительным и повторяющимся. Есть ли другой способ? Учитывая, что существует предсказуемый шаблон.
Я попробовал следующее, пока не осознал очевидную ошибку в этом подходе:
impl Display for TokenType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let thing = self.to_string();
let thing_split: Vec<&str> = thing.split("/(?=[A-Z])/").collect();
write!(f, "{}_{}", thing_split[0].to_uppercase(), thing_split[1].to_uppercase())
}
}
Метод to_string
становится доступным после реализации трейта Display
:
Этот трейт автоматически реализуется для любого типа, который реализует трейт Display. Таким образом, ToString не следует реализовывать напрямую: вместо этого следует реализовать Display, и вы получите реализацию ToString бесплатно.
Поскольку вы всё равно используете Debug
, вы можете использовать его:
impl Display for Thing {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,"{:?}",*self)
}
}
Ответ или решение
Программный подход к реализации трейта Display
для перечислений
При реализации трейта Display
для перечислений в Rust часто возникает необходимость избегать повторений и упрощать код. В этом ответе я предложу несколько вариантов, позволяющих минимизировать трудоемкость процесса, особенно если структура имен в перечислении следует предсказуемой модели.
Текущая реализация
Ваша текущая реализация трейта Display
выглядит следующим образом:
#[derive(Debug)]
enum Thing {
SomeThing,
AnotherThing,
}
impl Display for Thing {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Thing::SomeThing => write!(f, "SOME_THING"),
Thing::AnotherThing => write!(f, "ANOTHER_THING"),
}
}
}
Этот подход, хотя и работает, может быть довольно громоздким, особенно при добавлении новых вариантов перечисления.
Более программные подходы
-
Использование
Debug
для выводаЕсли вы уже используете трейты
Debug
, можно воспользоваться его функциональностью. Это уменьшит усилия по написанию кода, однако строки не будут настроены в нужном вам формате (например, с подчеркиванием).impl Display for Thing { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", *self) } }
Данный подход имеет свои ограничения, так как вывод может не соответствовать вашим требованиям по форматированию.
-
Создание макроса для генерации кода
Если ваше перечисление содержит множество вариантов, вы можете рассмотреть возможность использования макросов для автоматической генерации кода
Display
. Например:macro_rules! impl_display_for_enum { ($enum_name:ident, $($variant:ident),+) => { impl Display for $enum_name { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let name = match self { $( $enum_name::$variant => stringify!($variant), )+ }; write!(f, "{}", name.to_uppercase().replace('_', "")) } } }; } #[derive(Debug)] enum Thing { SomeThing, AnotherThing, } impl_display_for_enum!(Thing, SomeThing, AnotherThing);
Этот подход позволяет вам избежать повторений и сделать код более управляемым, особенно если в вашем перечислении много вариантов.
-
Использование
ToString
для простотыЕсли необходимо иметь строгий контроль над выводом, можно реализовать свою собственную логику форматирования. Однако следует помнить, что вызов
to_string()
может вызвать проблемы без предварительной реализацииDisplay
. Вместо этого вы можете создать функцию, которая будет возвращать форматированную строку для каждого варианта:impl Thing { fn to_custom_string(&self) -> String { match self { Thing::SomeThing => "SOME_THING".to_string(), Thing::AnotherThing => "ANOTHER_THING".to_string(), } } } impl Display for Thing { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_custom_string()) } }
Оба подхода обеспечивают более программный и менее повторяемый способ реализации трейта Display
. Каждый метод имеет свои нюансы, и выбор подхода зависит от конкретных требований вашего проекта.
Заключение
При реализации трейта Display
для перечислений в Rust возможно значительно упростить код с помощью макросов или применения трейта Debug
. Эти методы способствуют повышению читабельности и поддерживаемости кода. Не забывайте о требованиях вашего проекта и специфических форматах вывода при выборе подхода.