Picture

In the last chapter we looked at the interesting signature of the turn function. We will repeated it here.


# #![allow(unused_variables)]
#fn main() {
pub fn turn<Picture>(picture: Rc<Picture>) -> Rc<impl Fn(&Bx) -> Rendering>
where Picture: Fn(&Bx) -> Rendering 
#}

There is a lot going on. There is even a piece of information missing. I.e. Rendering is a type alias.


# #![allow(unused_variables)]
#fn main() {
pub type Rendering = Vec<(Shape, Style)>;
#}

Let's take some time to think and see what this signature is all about.

Shape & Style

Shape are primitive drawing instructions. Various sort of lines and instructions and where to place them. If you are really curious you can take a look at the documentation.

The basic shapes are used to provide more abstract drawing instructions such as letters or grids.

Style instructs an artist with which style to draw the shapes.

We will treat Shape and Style, and their derivatives, as black boxes. We will use them as is, without further inspection.

Rendering

Single stroke art is a thing, but in general art is made with more elements. A Rendering is just that, a sequences of shapes drawn with a certain style.

Rendering is a type alias. It offers a nice shorthand for a type expression, but is otherwise interchangeable with the long form.

Here is the definition


# #![allow(unused_variables)]
#fn main() {
pub type Rendering = Vec<(Shape, Style)>;
#}

Picture

Let's take a look at an other alias, this time defined in the signature of the turn function. For reference we repeat the definition below.


# #![allow(unused_variables)]
#fn main() {
where Picture: Fn(&Bx) -> Rendering
#}

A picture is a function that borrows a box, the one defined a few chapters back, and returns a Rendering, i.e. a sequence of shapes to draw.

This is the most important abstraction that we will introduce. All following types are in place to make these abstraction usable and safe.

Rc

It you take a look at the type signature of turn you will notice that the Picture is wrapped in a Rc. It is a

A single-threaded reference-counting pointer.

As can be seen from the Rc documentation.

The reason we need a reference counting pointer to the Picture here is two-fold.

Picture can not be owned

The first reason we need an reference counting pointer to the Picture is that we might want to reuse the picture. If you look back at Eschers square limit, you could see a lot of repetition in the image. As if a stamp is used to create the collage of fishes.

This means that we can not take ownership of Picture, because otherwise other parts of our program can not reuse it.

Picture can not be referenced

Usually a possible solution to not owning data is to take a reference. Unfortunately, that is not an option here. It is a bit involved, but it has to do with lifetimes.

In order for the Rust compiler to make some guarantees, the languages introduces lifetimes. The lifetimes of a piece of data is akin to a scope in which that data is valid.

Often Rust infers the lifetime for you, sometimes it can't. We opted for the easy way out.

Rc to the rescue

Luckily, with a reference counting pointer, these problems go away. We do trade something for it. We can't use our program in a multi-threaded environment. We could if we really wanted, but for this workshop we are not really interested in multi-threading.

The second is speed. Reference counting happens at run-time. This has a little overhead that we need to pay each time we run the program. This isn't a concern for us as well.

Blackbox

We have looked into the reason why Picture is defined as it is. With that knowledge under our belt, we continue with our goal. Recreating Eschers square limit.