Title: Hands-on Rust:
Always found the RGB::named(…) thing to be a bit verbose. Then I noticed that one of your examples just used WHITE, and BLACK, in their place. Now, back in chapter 9, I’m back to seeing RGB::named(…).
Title: Hands-on Rust:
Always found the RGB::named(…) thing to be a bit verbose. Then I noticed that one of your examples just used WHITE, and BLACK, in their place. Now, back in chapter 9, I’m back to seeing RGB::named(…).
My goodness, you’re right. That’s somewhat embarrassing on my end. It looks like I can get rid of the RGB::named
and just use the constants. (I just changed a few at random and everything still works; I’ll get this updated for the next beta). Thank you for that - it makes the code look a LOT nicer.
Some history for how I improved that without realizing I’d fixed it.
Early in bracket-lib development, the color constants were all defined as tuple triplets. For example:
pub const BISQUE: (u8, u8, u8) = (255, 228, 196);
I thought that was a bit unwieldy, because RGB back then was pretty dumb and wouldn’t work without the named
constructor. I implemented the From
trait for RGB, allowing it to be constructed with RGB::from(NAMED_COLOR)
or NAMED_COLOR.into()
. That was a bit better, and more Rusty.
Anyway, a while later the terminal gained support for alpha transparency. Suddenly, I needed RGBA
and not RGB
everywhere! So all of the terminal functions became generic parameters accepting any type of TryInto<RGBA>
- and conversion was added for RGB <-> RGBA. That was great, because you could use whichever one suited your problem domain and it would convert between them.
Using Into
and TryInto
gets a little complex, but it works remarkably well. The function signature for set
is as follows:
pub fn set<COLOR, COLOR2, GLYPH, X, Y>(
&mut self,
x: X,
y: Y,
fg: COLOR,
bg: COLOR2,
glyph: GLYPH,
) where
COLOR: Into<RGBA>,
COLOR2: Into<RGBA>,
GLYPH: TryInto<FontCharType>,
X: TryInto<i32>,
Y: TryInto<i32>,
{
See how it uses generics (like you do for Vec<T>
) with an additional where
constraint? The color fields will accept anything that knows how to convert into an RGBA type. (When you implement From
you get Into
for free - one of the few times Rust isn’t explicit). So what’s with the TryInto
? I wanted the user to be able to type any type of number they wanted, rather than having to remember that x
is an i32
and so on. Not all numbers are readily convertible - and some numbers may be converted for some values and not others. For example, converting a signed integer into an unsigned integer doesn’t make sense for a negative number. TryInto
attempts the conversion and throws an error our if the conversion fails at runtime.
It seriously never occurred to me that because RGB/RGBA have From<(u8, u8, u8)>
defined it now automatically accepted the color constants.
So thank you! I learned something and the book code will be easier to read.
(Edit: I should add that using traits and making your own is planned for the next beta. They are remarkably powerful)