top | item 47166191

(no title)

dminik | 3 days ago

Sort of. Given an instance (can even be a primitive) you can obtain a dyn reference to a trait it implements simply by casting it.

let a: i32 = 12

let b = &a as &dyn std::string::ToString; // i32 implements the ToString trait

let c = a.to_string(); // Static dispatch

let d = b.to_string(); // Dynamic dispatch through dyn reference

Note that there's not really any polymorphic objects in rust. All polymorphism in this case goes through the dyn reference which contains a pointer to a vtable for a specific trait.

Additionally, going from a dyn reference to a type-specific reference is not easy. Also, certain methods and traits are not dyn-compatible, mostly due to generic parameters.

The main use comes in with various libraries. Doing dynamic dispatch on a specific type is not very useful, but your library might expose a trait which you then call some methods on. If you accept a generic parameter (eg. impl Trait) each such invocation will cause monomorphization (the function body is compiled separately for each generic type combination). This can obviously bloat compile times.

Using a dyn reference in your API will result in only a single version being compiled. The downside is the inability to inline or optimize based on the type.

One additional use I found is that you can sometimes get around the divergent expression type in match expressions. Say you need to print out some values of different types:

let value: &dyn Display = match foo { A(numeric_id) => &numeric_id, B(string_name) => &string_name, C => &"static str", };

This would not work without dyn as each value has a different type.

discuss

order

menaerus|2 days ago

Ah, I see. Thanks for the example. I think I understand now. In C++ problem of monomorphization, or potential bloat due to excessive template instantiations, is normally handled at the linker level but can be controlled at the code level too through either by rewriting the code with some other type-erasure technique or simply by extracting bits of not-quite-generic-code into smaller non-generic entities (usually a non-templated base class).

Does this mean that the Rust frontend is spitting out the intermediate representation such that it doesn't allow the de-duplication at the linking phase? I see that Rust now has its own linker as of Sep 25' but it still works with normal linkers that are used in C and C++ too - gnu ld, lld, mold, ...

dminik|2 days ago

There's no custom rust linker just yet. The change in September was to switch from GNU LD to lld for performance on Linux. There are some rust linker projects (like wild), but these tend to be aimed at speed (and/by incremental linking) rather than executable size.

I'm not sure how useful deduplication at the linker level is in practice. Though I don't think Rust does anything different here than C++. The main issue I imagine is that the types used in generic code have different sizes and layouts. This seems to me like it would prevent deduplication for most functions.