Dynamic dispatch in Rust

Introduction

Recently I was able to break one of the walls I was hitting again and again during my journey into the world of Rust development. I'm developing in Python for a living. Rust is "just" for fun and out of curiosity. I was able to do the basic web dev stuff using Axum, Sqlx, ..., but I failed miserably whenever I tried some "non trivial" (which of course depends on your point of view) data strutures.

In hindsight the problem was not that some concepts were too complicated. There were just quite a few of them and I was not able to see the connection. Without the big picture it hard to compose them in the proper way. I feel like having made some progress in that regard and would like to share my experience to spare other people the same frustration. Here we go!

Preparing the stage

To demonstrate the problems I struggled with lets start with some code. The following snippet defines a trait and two structs. The trait is then implemented for both structs.

trait DoIt {
    fn do_something(&self);
}

struct A {}

impl DoIt for A {
    fn do_something(&self) {
        println!("Do it the way of A.");
    }
}

struct B {}

impl DoIt for B {
    fn do_something(&self) {
        println!("Do it the way of B.");
    }
}

This code compiles perfectly fine. Nothing fancy to see yet. But lets try to do something with it, based on the assumptions and expectations of an average OO programmer.

Traits, impl, dyn, ...

Here is a very naive approach for a kind of a factory that should return an instance of something that implements our trait:

fn kind_of_factory() -> DoIt {
    A{} //  might also be a B. who knows ...
}

This code does not compile. The error message of the compiler guide you in the right direction, but I was always confused about the difference between impl and dyn. What are they doing exactly? The above code compiles if we add an impl.

fn kind_of_factory() -> impl DoIt {
    A{} //  might also be a B. who knows ...
}

That looks good, but I was quite surprised to see the following code not compile.

fn kind_of_factory() -> impl DoIt {
    if (true) {
        A{} 
    } else {
        B{}
    }
}