Dyn traits in Rust in 2 hours

Crust of Rust: Dispatch and Fat Pointers

George Shuklin
ITIR
3 min readMay 2, 2021

--

I promised to write about things I read, turned out, some of them need video and audio.

The latest thing I was impressed by is video by ‘Crust of Rust’ about trait objects in Rust (and general dyn thing).

The link

Crust of Rust: Dispatch and Fat Pointers by Jon Gjengset.

Medium really wants to turn this link into embedded form, so I repeat it:

https://www.youtube.com/watch?v=xcygqF5LVmM

Why it’s good

For my Rust journey I kinda know they are, but never focused on them, moreover, I never used them (which is a sign I hadn’t known them).

In this short video (sorry 10min video lovers, but 2 hours is really short video for such huge topic) there was the mother of all explanation on dyn traits. Instead of somewhat magical thinking of Rust book, there was a truly engineering approach to explanation of constrains and reasons.

It laid an excellent foundation by not-afraid-to-go-to-the-pointers style. We can’t use unsized objects because we need to know how many bytes function argument takes. Boom. No magic here.

We can’t use generics in trait objects because generics requires monomorphization, and type erasure does not allow us to know, who and when would use trait objects, so we can’t monomorphize it.

What I’ve learned

dyn keyword in Rust is used to construct (or require to construct) a special type of object called ‘Trait Object’. By itself it is a tuple of two pointers — to the data (the structure values which were used to construct that object), and to the vtable — a call table to all functions associated with a trait. When any object (implementing specific trait) is turned to the trait object, only information about relevant (specific to the trait) calls are preserved. The calls are pointing to specific (concrete) implementation, which matches perfectly with data under data pointer.

Dyn traits allows dynamic typing in Rust. They are implementing ‘duck typing’ (if it’s Div, we can divide it, and we have no idea what that means for a specific type. A some kind of division, probably).

Moreover, they are really good at hiding generics from struct users. If we want to store a function pointer in a struct, there are two option: store a pointer to a dyn object, or make this structure generic.

I got confirmation for my intuitive description of str type (as something, you can point to, but can’t do anything with it), but in precise terms of been unsized.

(Of course, there is a lot more in those two hours, I doubt I can repeat it all).

Unexpected

One unexpected thing I found is that using &dyn Fn() can seriously reduce code size. If there are many generics, they are monomorphized, and if that function is large, and there are many type variants for monomorphization, the dyn may be a good solution.

Second: The existence of std::any, which is an interesting tool for introspection (which I though is missing in Rust).

Third one: That Rust allows structures of undefined size:

struct Foo{
foo: bool,
bar: [u8]
}

It’s not a box on a heap, it’s a structure which has no size. Funky.

--

--

George Shuklin
ITIR
Editor for

I work at Servers.com, most of my stories are about Ansible, Ceph, Python, Openstack and Linux. My hobby is Rust.