Archives for posts with tag: programming

So I saw this post asking for a translation of these Java design patterns to Rust. I will present a quite literal translation of the factory pattern and comment on its Rust idiomaticity in the progress. You can find the full code here. Be sure to have a look at all the commits.

The Java Factory Pattern

This section uses Java terminology.

The example contains an interface “Shape” with the method “draw”. Three classes (“Circle”, “Square”, “Rectangle”) implement that interface. Now I don’t find this example very well-chosen, as one could argue wether the “Square” class should be a subclass of the “Rectangle” class. In the example they are not subclasses and the geometrical relation between squares and rectangles is not used in any way. So to remove unnecessary confusion I will replace “Square” by “Triangle” in the following.

Furthermore we have a class “ShapeFactory” with a non-static method “getShape”, which receives a String and creates an instance of a class for it (e.g. String “circle” will result in a Circle object being returned). Then there is the main class “FactoryPatternDemo”, which serves the sole purpose of containing the main method, the start of the execution.

The Literal Translation

Shapes

A Java “interface” can be translated to a Rust “trait”. They both define common methods to interact with different things.

A Java “interface” defines “methods”. “Methods” are functions, which always get a reference to an instance of their associated class as the first parameter “this”. If a “class” has all these methods, with the same type signature as defined by the interface, the class “implements” that interface (you also have to declare that it does so).

A Rust “trait” defines functions. These functions often take as first argument a “self” in some way (as a reference “&self”, as a mutable reference “&mut self” or by taking complete ownership “self”). This is optional tough. If there is an “implementation” of a trait for a “struct” (or “enum”), that “struct” (or “enum”) “satisfies” that trait.

So let’s look at some code:

trait Shape {
    fn draw(&self);
}

struct Rectangle;
struct Circle;
struct Triangle;

impl Shape for Rectangle {
    fn draw(&self) {
        println!("Inside Rectangle::draw function");
    }
}

impl Shape for Circle {
    fn draw(&self) {
        println!("Inside Circle::draw function");
    }
}

impl Shape for Triangle {
    fn draw(&self) {
        println!("Inside Triangle::draw function");
    }
}

The lines with “struct” would usually contain information about the representation of the respective objects. A more realistic example could look like this:

struct Rectangle {
    origin: (f64,f64),
    size: (f64,f64),
}

But this is besides the point, as the original Java pattern PDF also uses dummy objects without member data.

The Factory

In the Java version there is a class “ShapeFactory” with a method “getShape” which returns an object of type “Shape”

Java uses dynamic dispatch by default. This means I can have a variable with the type “Shape” and it will check at runtime which “draw” function to call. Rust doesn’t like to do things at runtime (because it makes program execution slower), so dynamic dispatch is not enabled by default.

In Rust, there can’t be a variable with the type “Shape”! But Rust supports dynamic dispatch, if we really want it. We just have to box the variable: A variable of type “Box<Shape>” is perfectly fine.

But there is another pitfall with the return type of getShape: If the given string matches none of the available shapes, the factory will return “null”. In Java every function which is supposed to return an object can return “null” instead. In Rust we are only allowed to do that if we explicitly say so in the type of the function. Instead of returning a “Thing” we must return an “Option<Thing>” (Option documentation). So the Java return type of “Shape” becomes “Option<Box<Shape>>”.

struct ShapeFactory;

impl ShapeFactory {
    pub fn new() -> ShapeFactory {
        ShapeFactory
    }
    pub fn get_shape(&self, shape_type: &str) -> Option<Box<Shape>> {
        let shape_type_uppercase = shape_type.to_uppercase();
        match shape_type_uppercase.as_ref() {
            "CIRCLE" => Some(Box::new(Circle)),
            "RECTANGLE" => Some(Box::new(Rectangle)),
            "TRIANGLE" => Some(Box::new(Triangle)),
            _ => None,
        }
    }
}

The Main Function

In Java there are no functions. Everything is a method. To start execution, there is a class which has a method named “main”. Execution will start there. In Rust execution starts at the function named “main”. I will not literally translate this Java-madness OOP-for-the-sake-of-it main method to a Rust struct, but instead I will keep my sanity and translate the Java main method to the Rust main function.

Also instead of implicitly breaking the whole program in the case that we get a “None” (Rust’s version of Java’s “null”), we actually have to handle that case in Rust, because Rust does not like failing implicitly.

fn main() {
    let shape_factory : ShapeFactory = ShapeFactory::new();
    let shape1_option : Option<Box<Shape>> = shape_factory.get_shape("CIRCLE");
    match shape1_option {
        Some(shape1) => {
            // shape1 has type Box<Shape>
            shape1.draw();
        }
        None => {
            panic!("No shape found!");
        }
    }
}

Splitting it up

In the Java example everything is split up in multiple files. We just put the code in one file so far. Let’s change that.

Making it More Idiomatic

Throwing out the Factory

In the above version, we use the ShapeFactory as an object. It has only one method and no internal state. In rust we would definitely just use a function for that and throw out the ShapeFactory.

pub fn get_shape_by_name(shape_type: &str) -> Option<Box<Shape>> {
    let shape_type_uppercase = shape_type.to_uppercase();
    match shape_type_uppercase.as_ref() {
        "CIRCLE" => Some(Box::new(Circle)),
        "RECTANGLE" => Some(Box::new(Rectangle)),
        "TRIANGLE" => Some(Box::new(Triangle)),
        _ => None,
    }
}

Or even not have that function at all and just directly construct what you want (e.g. Rectangle). To create more complicated things, we use the builder pattern in Rust.

Throwing out Dynamic Dispatch

Dynamic dispatch is a nice feature and in most situations its performance penalty is not even noticeable. If execution speed is of importance, we want to avoid dynamic dispatch. Rust provides means to write generic code but at the same time avoid dynamic dispatch.

fn show_shape<T:Shape>(shape: &T) {
    println!("Have a look at this marvelous shape:");
    shape.draw();
    println!("Amazing!");
}

fn main() {
    let shape2 = triangle::Triangle;
    show_shape(&shape2);
}

The function “show_shape” is defined for any type that satisfies the trait “Shape”. The compiler will see what type the actually used shape object has and select the respective function call at compile-time! It cannot be used on a “Box<Shape>”! Repeat, the following does not work:

// shape1 has type Box<Shape>
show_shape(&shape1);

Summary

Rust supports many different programming styles. You can program very javaesque code with it and it won’t complain. It will just run a little slower, which is probably fine for 99% of all code. If you need some fast code, don’t try to translate what you would do in another language to Rust; read about Rust, read Rust code, write Rust code and learn to think in a Rusty way for optimal results!

To learn how things work in Xlib and XI2 programming, I’ve written a little game. You can attach multiple keyboards to one computer and then invite some friends over to see, who can type fastest. The code is really smelly because I did this only for learning purpose. If you like it, feel free to make the code and/or the game better (like bigger font size or more player support or something).

You can get the code on github.

screenshot

screenshot

another screenshot

another screenshot

UPDATE: The code is ported to Qt (better font rendering, fullscreen…) and can be found here: https://www.ralfj.de/git/multypo.git. Qt does not support distinguishing key presses between different keyboards, so the event handling code is still X specific.