Printing II: Printable
Now it is time to make our Color
struct printable.
Here it is again:
use std::*;
struct Color {
r: u8,
g: u8,
b: u8,
a: u8
}
To advertise its printablility, we need to implement the Fmt
trait:
use std::*;
use std::fmt::*;
struct Color {
r: u8,
g: u8,
b: u8,
a: u8
}
impl Color: Fmt {
fn dyn_fmt(self: &Color) -> DynFmt {
todo()
}
fn fmt(self: &Vehicle, fmt: &Formatter, stream: &FormatStream) {
todo()
}
}
Tne Fmt
trait has two methods: fmt
does the formatting and dyn_fmt
packs the format function together with the object itself together,
since all type information is lost when passing it to the println
function and otherwise we wouldn't know how to format it
use std::*;
use std::fmt::*;
struct Color {
r: u8,
g: u8,
b: u8,
a: u8
}
impl Color: Fmt {
fn dyn_fmt(self: &Color) -> DynFmt {
_ { object: typecast(self), fmt: Color::<>::fmt }
}
fn fmt(self: &Color, fmt: &Formatter, stream: &FormatStream) {
stream.write_str("Color { r: ").write(fmt, &self.r)
.write_str(", g: ").write(fmt, &self.g)
.write_str(", b: ").write(fmt, &self.b)
.write_str(", a: ").write(fmt, &self.a)
.write_str(" }");
}
}
fn main() {
let c = Color { r: 255, g: 150, b: 100, a: 255 };
println("We have the color $", c.dyn_fmt());
}
Note: We try to avoid using
format
orformatln
infmt
, since that would allocate a string. All theFormatStream::write*
methods are allocation-free
Now we can comfortably print our Color
anywhere:
use std::*;
use std::fmt::*;
struct Color {
r: u8,
g: u8,
b: u8,
a: u8
}
impl Color: Fmt {
fn dyn_fmt(self: &Color) -> DynFmt {
_ { object: typecast(self), fmt: Color::<>::fmt }
}
fn fmt(self: &Color, fmt: &Formatter, stream: &FormatStream) {
stream.write_str("Color { r: ").write(fmt, &self.r)
.write_str(", g: ").write(fmt, &self.g)
.write_str(", b: ").write(fmt, &self.b)
.write_str(", a: ").write(fmt, &self.a)
.write_str(" }");
}
}
fn main() {
let c = Color { r: 255, g: 150, b: 100, a: 255 };
println("We have the color $", c.dyn_fmt());
}
It is also possible to have more print methods on the same struct, albeit not under the Fmt
trait any more, which only houses the canonical print
Here we convert the example from the methods section to the allocation free print api
use std::*;
use std::fmt::*;
struct Color {
r: u8,
g: u8,
b: u8,
a: u8
}
impl Color: Fmt {
fn dyn_fmt(self: &Color) -> DynFmt {
_ { object: typecast(self), fmt: Color::<>::fmt }
}
fn fmt(self: &Color, fmt: &Formatter, stream: &FormatStream) {
stream.write_str("Color { r: ").write(fmt, &self.r)
.write_str(", g: ").write(fmt, &self.g)
.write_str(", b: ").write(fmt, &self.b)
.write_str(", a: ").write(fmt, &self.a)
.write_str(" }");
}
}
impl Color {
fn dyn_ansi_fg(self: &Color) -> DynFmt {
_ { object: typecast(self), fmt: Color::<>::ansi_fg_fmt }
}
fn ansi_fg_fmt(self: &Color, fmt: &Formatter, stream: &FormatStream) {
stream.write_str("\x1b[38;2;").write(fmt, &self.r)
.write_str(";").write(fmt, &self.g)
.write_str(";").write(fmt, &self.b)
.write_str("m");
}
fn dyn_ansi_reset() -> DynFmt {
// dummy object since this is a static method
_ { object: c_api::null, fmt: Color::<>::ansi_reset_fmt }
}
fn ansi_reset_fmt(self: &Color, fmt: &Formatter, stream: &FormatStream) {
// Note: do not access `self` since its a null dummy value
stream.write_str("\x1b[0m");
}
}
fn main() {
let c = Color { r: 255, g: 150, b: 100, a: 255 };
println("We have the color $", c.dyn_fmt());
println("Look, $colorful text$!\n", c.dyn_ansi_fg(), Color::<>::ansi_reset());
}