Why does rustc not automatically infer `dyn` for trait objects?

After some headache, I learned & understood I can manage trait objects in Box<dyn MyTrait>. However, I cannot imagine a case in which the size of the MyTrait object can be known.

So, why do I need to write this explicitly? The rust compiler obviously knows MyTrait is the identifier of a defined trait, and could thus infer it has dynamic size (?)

1 Like

It used to be the case that a trait object could be referred to using the name of the trait, without dyn. The dyn was added, and later made mandatory in the 2021 edition, because otherwise it is very hard for a programmer reading the code or its documentation to know what is a concrete type and what is a trait denoting a trait object. Being explicit about things like this is an important part of Rust's style.

9 Likes

Here's the RFC: 2113-dyn-trait-syntax - The Rust RFC Book

The TLDR seems to be that when impl Trait was added (in argument and return type positions) bare Trait became confusing and would favor the often worse option. It also helps when talking about the trait Foo vs the type dyn Foo.

6 Likes

Thanks, understood :slight_smile:

Before this change was made, many beginners were confused by trait objects, using them unintentionally because they did not yet understand that a treat is not a type. Now, we don't get those questions.

I cannot imagine a case in which the size of the MyTrait object can be known … could thus infer it has dynamic size

Also note that the dyn wasn't intended to stand for “dynamic size” but for “dynamic typing / dynamic dispatch”.

5 Likes

Regardless of that argument being in the RFC, dynamic typing can only be emulated with trait objects, and thinking dyn Trait is dynamically typed is still a common newcomer hurdle. I think conflating "the notion of dynamic typing" with dyn Trait is more confusing than illuminating, even if it can be used as a crutch when first learning the language.[1]

Finally getting it through my head that dyn Trait is its own statically-known type (and understanding how that works) was one of my intermediate level "aha!" moments. It would have came sooner and with less bafflement if tutorials and the like didn't draw that association.[2]

(I still view the dyn prefix as a very good thing, even if a different prefix might have been less confusing.)


  1. IMO most beginner texts, including the book, have this slightly-askew too-simple introduction to many facets of Rust, resulting in mental-model debt you have to gradually pay off after you've got your Rust feet under you. The naming of things in language itself is partially to blame. ↩︎

  2. Or perhaps if they did so while very clearly demonstrating the difference, versus something closer to "this is how you do dynamic typing in Rust". ↩︎

2 Likes

At least for me, when I was learning Rust I got confused thinking that I could pass a trait as a generic. By having a "turn a trait into a type" operator, it helps mitigate confusion like that, and when there are multiple ways to do that (impl & dyn), saying which one you meant can be helpful.

There's lots of things that Rust has enough information that it could do -- widening conversions, dereferencing in assignments, type inferring return types, ... -- that it chooses to make visible in the code instead for various reasons.

(It's not exactly the usual meaning of "operator", but I tend to think of dyn as that, at least when speaking informally.)

Also note that there's nothing to be "inferred" here.

In fact, dyn Trait used to work without the dyn. There's nothing profound about the dyn keyword. It doesn't add any more meaning, nor does it change the existing meaning of code. It's not a type, it's not a trait, it doesn't grant or remove capabilities from what a trait object could do if it were spelled just Trait. It might as well just not exist at all. It's nothing.

It's basically a neuroactive drug: it only affects your brain. It makes you see more obviously that the code is talking about a trait object, so it removes confusion by adding explicitness. The compiler doesn't need it; you do.

4 Likes

This might be my vestigial C++ brain, but I'm somewhat confused by what dynamic typing could mean in a statically typed language except a vtable-style approach like dyn Trait / interfaces.

Are you thinking something like C#'s dynamic keyword, generating new type-dependant code at call time?

I guess this is a somewhat artificial and not very useful distinction. One could trivially define a dynamically-typed language as statically-typed and type-inferred where every type is implicitly "union of everything". This is completely impractical, though.

In contrast, viewing dyn Trait as dynamic typing is useful. It's functionally equivalent with dynamic typing, after all, but still there has to be some static type assigned to a value, since the language is statically typed.

That's why C# dynamic is interesting: it lugs a bunch of the compiler type checking into runtime, in an otherwise very static language. Interesting, however, doesn't mean good...

If you start from "everything is statically typed and that's irrefutable",[1] the chances for confusion are surely less. But not everyone starts from there, especially when a lot of official or semi-official sources are throwing around technically inaccurate terminology.

I definitely don't consider them equivalent.


  1. and knowledge of vtables etc... ↩︎

Right, but vtables are not equivalent to what is what I meant to ask: I might not be understanding what point you're actually saying though. Are you saying that they might think this should work?

fn handle(animal: &dyn Animal) {
  if animal.name() == "mittens" {
    animal.purr(); // surely only a Cat would have such a name 
  } else {
    animal.bark(); // there's only two types of Animal
  }
}

I haven't seen this confusion myself, but I guess it could happen. I'm not sure why the dyn keyword would have much effect either way, though?

1 Like

It doesn't,[1] so this sidebar (which I created) is off topic. My comment was a reaction after having to clear up how dyn Trait works time after time.[2]

I didn't have one particular misunderstanding in mind. Here's one where (as best I could tell) the poster was under the impression that dyn BufRead carried around some sort of type information and that Box<dyn BufRead> could be unboxed and downcast.


  1. other than the naming choice of dyn might aid in being misleading for some, ala mut ↩︎

  2. And my own history learning about it. ↩︎

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.