Code: https://github.com/caveofprogramming/rust/
What is Dynamic Dispatch?
In object-oriented languages we can normally create subclasses of a superclass type, then have a variable of the superclass type which can be made to refer “dynamically” at runtime to whichever of the subclasses we like.
For example, we could have an Animal
superclass, and Dog
and Cat
subclasses.
Dog
and Cat
are then kinds of Animal
.
Suppose Animal
has a speak()
method. Dog
and Cat
then inherit this method, but they can override it, so that each has its own version of speak()
with different functionality.
It’s then possible to write a program which, for example, asks the user what kind of Animal
to create, and sets a variable of type Animal
to refer to whatever they’ve chosen.
When the speak()
method of this method is called, the correct version of it must then by called by the program at runtime. If the user chose to create a Dog
, the Dog
speak()
method is called; if they chose a Cat
, the Cat
speak()
method is called.
This is known as dynamic dispatch. The appropriate method is dynamically dispatched at runtime.
How This Works In Rust
Rust is a kind of semi-object-oriented language. It doesn’t have inheritance, so we can’t create a superclass object and then have subclasses of it.
However, it does have traits, which define functionality. So it is possible to have an Animal
trait and then Dog
and Cat
structs which implement this trait.
Then we implement the trait for these classes. If Animal
defines a speak()
method, Dog
and Cat
must have one.
It is then possible, in Rust, to do dynamic dispatch, even just using ordinary references.
However, I ran into some problems with this when I wanted to create objects in the new() function of some other struct, as you’ll see in the video.
These problems can be solved using the reference counter, Rc
.
In addition, when you specify a trait as the type of a variable, you must make the variable a reference and use the dyn
keyword, which is specifically intended to allow dynamic dispatch, apparently. Otherwise your program won’t compile.
Dynamic Dispatch with Ordinary References
First let’s take a look at dynamic dispatch using only references.
Here’s our trait.
Dog and Cat are then just structs which implement this trait.
Give this, the following actually works perfectly well:
The problem is that this doesn’t work when we want to create the struct instances in a method, such as new()
, and store references in struct fields, because then we end up with dangling references to objects that have gone out of scope.
Then we need reference counters, and I cover this in the video.
Let’s take a look here at a simple example of dynamic dispatch using reference counters.
Dynamic Dispatch with Reference Counters.
We’re going to need
at the top of the file.
Then we can do the following, which can also be made to work with struct methods and fields.
Why Don’t Plain References Work with Struct Fields?
This issue I had with plain references was that I wanted to have two struct fields referring to a Dog
and a Cat
then a third, “pet
” that refers to one of the above, and I wanted to arrange this in a function called new() that’s associated with the struct.
One you’ve directly initialised a struct field with a Dog
, for example, you can’t then make pet
refer to that field via self, because self doesn’t exist yet.
You can’t create dog and cat local variables and just use references to those either, because then you’re left with dangling references.
Try it and you’ll see what I mean!
However, Rc
fixes the problem.
If you need your objects to be mutable, you can use RefCell
for interior mutability and use types like Rc<RefCell<dyn Animal»
.
If you need to pass objects between threads you could also consider
Arc<Mutex<dyn Animal»
or
Rc<RwLock<dyn Animal».