- A common requirement in programming is to display
the contents of an object on the console.
Here's one approach.
I've got a simple structure called coord with longitude
and latitude.
I've implemented in the coordinate structure,
I've implemented a function,
I've chosen to call it to_string,
nothing special but the name.
It takes a coordinate, and it uses the format macro,
remember it basically builds a formatted string containing
the longitude and the latitude.
That formatted string gets returned.
So, I could invoke the to_string function like this,
in my code I could say a chord dot two-string,
and it'll basically print out the longitude
and the latitude like so.
Right, well, that would work,
but a better approach in Rust that's more kind of in keeping
with the Rust philosophy,
there's an interface or trait called display,
which basically is invoked when you output objects like so.
So, the display trait has a single function
called fmt format.
It takes a reference to the object
whatever implements this display trait, like coordinate.
It also takes an object called a formatter.
The formatter provides an API to help you do the formatting.
One of these objects gets created for you automatically,
as you'll see, and effectively it returns a result.
It's the string result that you want to then display.
Right, so this trait, you can implement this trait
in your structures like this.
My coordinate structure can implement the display trait.
Therefore, it has a format function,
it receives a reference to the coordinate,
and it receives a format object, and it returns a result.
If you do implement the display trait,
therefore if you have a format function,
then you can output a coordinate just by saying this.
You can just say a code,
and internally the compiler will convert
that into a call to the format function.
So, if you write a code like this
and you pass it into the default formatter,
internally the compiler will convert this
into a statement like this.
It'll take your coordinate object,
and it'll implicitly call the format function.
It'll pass in a formatter object,
which we don't need to worry too much about,
it just happens.
And the idea is that your format function returns a string,
or a result, which is really a string,
and whatever you return,
that's what's gonna be displayed.
So, let's see how to actually do this in practice.
Here is my example.
So, I've imported from the stud format module,
I've imported the display trait, the formatter structure,
and the result structure, which I'll need.
Here's my coordinate, it has a longitude, latitude,
and I've implemented the display trait for coordinate.
In other words, I have a format function.
It receives a coordinate reference plus a mutable formatter,
it returns a result.
So, one technique, there's a macro called write,
and effectively what it does,
it's a bit like writing to a file,
it writes a formatted string into the formatter.
Okay, so the formatter is kind of like the destination
and into that formatter, you can write whatever you want.
I've written a string coord square bracket curly brackets
here would be the longitude these curly brackets
would be the latitude n square bracket.
Okay, so by implementing the format function
like this whenever I output a coordinate it'll use this code
to display it.
Right now, there is another way
to implement the display trait.
The formatter class that we've just been looking
at has some helper methods
to help you build a formatted string.
So, for example, the formatter structure
has a debug list method.
It helps you to generate a string containing the formatting
for a list, like an array.
Technically when you call the debug list function,
it returns a data type called debug list.
I'll talk about that again in a moment.
There's also a function on formatter called debug map.
If you have a hash map, for example, you'd call this method.
You'd say formatter debug map
and it returns a debug map object,
and I'll explain that again in a moment.
There's a debug set.
If you had a set,
like a set that contains distinct collections,
no duplicates, the debug set method
would return a debug set object
which helps you output a set.
There's a debug struct method,
that's what we're going to use.
If you have a structure, you can say formatter.debugstruct.
It'll return a debug struct object,
which helps you format the output,
or the content of a structure.
There's also a debug tuple
which helps you output the content of a tuple.
So, you call one of these methods on your formatter,
and it returns one of these data types debug list,
or debug map or debug something,
and these debug types I've called it debug XXXX here,
these debug structures here all of them have methods
which help you format that type of thing.
So, for example,
debug struct has methods called field and finish.
If you call formatter debug struct,
and you get back one of these objects,
the debug struct type then has these methods
to output the fields of a structure,
and then you can say,
finish when you've finished outputting everything.
It's basically a builder pattern.
So, that might sound a bit abstract.
Let me show you how that actually looks in practice.
This is an alternative implementation for display
for the coordinate.
Here, I've taken the formatter object,
and I've called that debug struct method that I mentioned
on the previous slide.
You can give it like a header
to say it is a coordinate structure.
This is just verbiage.
This is literal output that you want to appear
on the console.
So, this is literally what you want to output.
Coordinate structure, and then you can say there's a field,
and then you can say the name of the field
in whatever text you want,
and then the value that you want to output.
Another field, these strings here are what's going
to appear on the console,
and these are the values to output next to those strings.
So, it'll output coordinate structure,
longitude and the value,
latitude, and the value, and then finish.
Okay, so by calling these methods you're building the output
to appear and then that's what you return.
So, both of those techniques using either the right macro,
or these helper methods to build the format, you know,
step by step, either of those techniques are fine.
So, let's see a look in the project.
We're going to look at the main RS, the entry point.
We'll have a look at the displayable,
that's the structure which implements the display trait,
and then we'll run the demo displayable code
to actually execute the demo.
Okay then, so from the top here's my main code.
We're going to run demo displayable, and, in my structure,
I've got a module called displayable.
Okay, just so that the name of it is fairly obvious.
Here's my coordinate structure,
the data, longitude, latitude.
I've got some regular implementation.
I've just got to build a function
to create a coordinate just to initialize one.
So nothing particularly new there.
And then I've implemented display trait for create.
I've actually got two alternative implementations.
First of all, I've got the implementation
that we looked at first,
where it takes a formatter and it writes
to that formatter whatever string
with whatever values you want to output,
the longitude and the latitude.
So, we'll run the code using this formatter first.
So, demo displayable.
This is the code we're gonna look at.
It creates a coordinate
and then outputs it using the formatter,
using the display trait implementation.
So, at the moment, when I output this object,
it'll call the format function in my display implementation.
And currently, this is the format function
that it's going to see.
And that's what we'll see on the screen.
So, if I say cargo run,
it'll say coordinate, square bracket,
and then the longitude and the latitude, 51 degrees north,
3.9 degrees west.
If I'm not mistaken,
that would be the coordinates of Swansea,
who would have guessed that?
Okay, right.
Well, let's comment out this implementation
and let's uncomment the alternative implementation.
In this one, I'm using the debug struct method
on the formatter and that's the string that will appear.
Followed by these fields with that text
and that's what's going to be output.
So, let's run that now.
Cargo run.
So, this time in my code,
this time when it calls the format method on my coordinate,
it'll be calling this format method
and outputs coordinate structure.
whatever text you pass in here is literally going to appear,
so it's up to you, you could output it in French, or German,
or even Welsh if you knew the words,
and then it outputs longitude field,
and the latitude field using those words there,
so it put the curly brackets in automatically,
and then we just slotted in the text that we want to appear,
and then the value and the text we want to appear,
and the value.
Okay, so this is a very idiomatic way of doing it in Rust.
Rust defines several interfaces or traits which allow you
to hook in to the standard ecosystem
to cause a display formatting like so.