9

I have two enums, NormalColour and BoldColour, both of which implement the Colour trait. They contain Blue, BoldGreen, and so on.

I'd like to return values of both of these types from the same function, treating them as though they're just a Colour value, calling the paint function on the result, but I can't find a way to coerce the Rust complier into doing this for me. I'd like to be able to write something like this:

pub trait Colour {
    fn paint(&self, input: &str) -> String;
}

fn file_colour(stat: &io::FileStat) -> Colour {
    if stat.kind == io::TypeDirectory {
        Blue
    } else if stat.perm & io::UserExecute == io::UserExecute {
        BoldGreen
    } else {
        White
    }
}

What type do I have to make the function return for it to work?

I'll eventually like to make more types implement Colour, which is why I'm not interested in just turning the two enums into one big enum.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Ben S
  • 1,415
  • 3
  • 14
  • 29

2 Answers2

7

The answer is trait objects. This means that you will work with Box<Colour> as your type; bare Colour is not an instantiable type. You can cast Box<T> objects to Box<Colour> with the as operator: Box::new(NormalColour::White) as Box<Colour>. In many places this is not necessary (just write Box::new(NormalColour::White) and it can be automatically coerced to Box<Colour>), but sometimes it will still be necessary.

Still, if you can do it as an enum, that will probably be a nicer solution.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • 1
    How does the signature look like? – Juan Ibiapina Aug 09 '15 at 22:16
  • @JuanIbiapina: it was a return type of `~Colour` at the time; now, that is `Box`. `&'static Colour` would probably work as well, as a matter of fact. – Chris Morgan Aug 09 '15 at 22:21
  • @ChrisMorgan I have a set of object types in my design that could be related through a common trait, and an enum would be almost a tragedy because there would be around 50-75 members in the enum. Also I'd want to return an Option for the case where the remote guy misspelled the name. Can you suggest something? – cardiff space man Oct 30 '15 at 17:05
  • @cardiffspaceman: lots of members in the enum isn’t a disaster by any means. For the rest of your question, I haven’t got a clue what you mean. And it’s a separate question from this and not appropriate for a comment on an answer of another question. – Chris Morgan Nov 05 '15 at 09:47
2

The answer above suggests:

fn file_colour(stat: &io::FileStat) -> Box<Colour> { /* ... */ }

which works, but you need to wrap all of the returns in a Box::new() call.

However, in Rust 1.26 you can now say

fn file_colour(stat: &io::FileStat) -> impl Colour { /* ... */ }

and simply return the value. No Box needed.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Sean Perry
  • 3,776
  • 1
  • 19
  • 31