【问题标题】:How can I Display an enum in lowercase?如何以小写形式显示枚举?
【发布时间】:2021-10-31 00:59:54
【问题描述】:

我有一个枚举:

pub enum BoxColour {
    Red,
    Blue,
}

我不仅要get this value as a string,还希望将值转换为小写。

这行得通:

impl Display for BoxColour {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(match self {
            BoxColour::Red => "red",
            BoxColour::Blue => "blue",
        })?;
        Ok(())
    }
}

当颜色列表增加时,需要更新此列表。

如果我使用write! 宏,似乎无法操纵结果,因为write! 返回() 的实例而不是String

impl Display for BoxColour {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{:?}", self)
    }
}

这表明这是通过副作用工作的,也许我们可以破解内存中值所在的相同位置,但即使可以,这也可能不是一个好主意...

【问题讨论】:

  • write! 返回一个 () 的实例——它没有。它返回一个fmt::Result,如返回类型fmt所示。
  • ...而fmt::ResulttypeResult<(), fmt::Error>
  • 您已经回答了您提出的问题(“如何以小写形式显示枚举?”)。请明确说明您在问什么。
  • you've linked to 的问题有一个使用 strum 的答案,strum allows you to customize what the Display format will be。您似乎已链接到合适的解决方案。
  • @Shepmaster,感谢您的回复。 @Smitop 正确理解了我的意图(如下)。是的,我的意思是指出Result 的成功路径是() 而不是String 或者我可能知道如何使用的东西。我看到了strum,但也无法让它发挥作用,但这是另一天的问题。

标签: rust enums display lowercase write


【解决方案1】:

strum crate 提供了一个派生宏,用于为枚举实现Display,并可选择小写变体名称:

use strum_macros::Display;

#[derive(Display)]
// If we don't care about inner capitals, we don't need to set `serialize_all` 
// and can leave parenthesis empty.
#[strum(serialize_all = "snake_case")]
pub enum BoxColour {
    Red,
    Blue,
    LightGreen,  // example of how inner capitals are treated
}

fn main() {
    for c in [BoxColour::Red, BoxColor::Blue, BoxColor::LightGreen] {
        println!("{}", c);
    }
}

您还需要在Cargo.toml 中对strum 进行相应的依赖:

[dependencies]
strum = { version = "0.21", features = ["derive"] }

这应该打印出来:

red
blue
light_green

strum 将生成类似于您提到的 matchBoxColour::Red => "red", 案例的代码,但无需手动更新。

【讨论】:

  • 凯文·里德,为响应干杯。它没有“按原样”工作,但我已经修复了它。 :-) 有些人可能会对添加依赖项提出质疑,但不可否认,这比@Smitop 的解决方案更优雅。我不确定 Stackoverflow 是否允许我更改接受的答案,但我会尝试。 :-)
  • @BrianKessler 您可以随时更改,接受的答案只是“我选择解决我的问题的方法”提示未来的读者,不用担心。
【解决方案2】:

作为@Smitop 答案的扩展,通常可以使用自定义格式化程序以小写形式显示任何值,例如:

use std::fmt::{self, Write};

struct LowercaseFormatter<'a, 'b>(pub &'a mut fmt::Formatter<'b>);

impl<'a, 'b> fmt::Write for LowercaseFormatter<'a, 'b> {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        for ch in s.chars() {
            self.0.write_fmt(format_args!("{}", ch.to_lowercase()))?;
        }
        
        Ok(())
    }
}

然后这样使用它:

#[derive(Debug)]
pub enum BoxColour {
    Red,
    Blue,
}

impl fmt::Display for BoxColour {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(LowercaseFormatter(formatter), "{:?}", self)
    }
}

fn main() {
    println!("{}", BoxColour::Red); // "red"
}

Playground

这个版本并没有为每个格式分配一个字符串,但也不是很优化,为每个字符调用write_fmt相对成本较高。

ascii 字符的一种替代方法是调用write_char(ch.to_ascii_lowercase()),但一次写入一个字符的字符串也相对昂贵。

适当的解决方案是以某种方式对字符串进行分区,以便能够一次写入所有已经小写的字符,并且只写入大写字符。

【讨论】:

    【解决方案3】:

    这是一种无需在每次添加新颜色时手动更新Display::fmt 的方法,方法是使用派生的Debug 实现并将其小写:

    #[derive(Debug)]
    pub enum BoxColour {
        Red,
        Blue,
    }
    
    impl fmt::Display for BoxColour {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            write!(formatter, "{}", format!("{:?}", self).to_lowercase())
        }
    }
    

    (playground)

    注意write! 返回一个Result&lt;(), fmt::Error&gt;(这是一个fmt::Result),不是一个原始的()

    但请考虑手动更新列表或使用宏来指定是否会更好。还要考虑如何将带有多个单词的颜色(例如LightBlue)小写:lightblue 是你想要的吗?

    【讨论】:

    • 您格式化发送给格式化程序的参数?感觉很糟糕,很糟糕
    • @Stargateur 代码确实看起来很hacky,但是嵌套格式是使调试输出小写的唯一方法:没有任何通用小写格式说明符(LowerHex/LowerExp 存在但仅适用于格式化数字)。
    猜你喜欢
    • 1970-01-01
    • 2011-10-24
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 2014-02-13
    • 1970-01-01
    • 2012-11-14
    • 2014-11-10
    相关资源
    最近更新 更多