'How can I display an enum in lowercase?

I have an enum:

pub enum BoxColour {
    Red,
    Blue,
}

I not only want to get this value as a string, but I want the value to be converted to lower case.

This works:

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(())
    }
}

When the list of colours grows, this list would need to be updated.

If I use the write! macro, it does not seem possible to manipulate the result because write! returns an instance of () instead of a String:

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

This suggests that this is working through side effects and maybe we could hack the same place in memory the value is going, but even if that is possible, it probably isn't a good idea...



Solution 1:[1]

As an extension to @Smitop's answer, it is possible to generally display any value in lowercase by using a custom formatter, as such:

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(())
    }
}

Then use it as such:

#[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

This version doesn't allocate a string on every formatting, but it is also not very optimized, calling write_fmt for every character is relatively costly.

One alternative for just ascii characters, is to call write_char(ch.to_ascii_lowercase()), but writing a string a character at a time is also relatively costly.

A proper solution would partition the string in some way to be able to write all already lowercase characters all at once, and only special case the uppercase ones.

Solution 2:[2]

Here is a way to do that without needing to manually update Display::fmt every time you add a new colour by using the derived Debug implementation and lowercasing that:

#[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)

Note that write! returns a Result<(), fmt::Error> (which is a fmt::Result), not a raw ().

But consider if it would just be better to manually update the list, or use a macro to specify that. Also consider how colours with multiple words (such as LightBlue) should be lowercased: is lightblue what you want there?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Filipe Rodrigues
Solution 2 Smitop