How can I tell a Type from a Trait by just looking at the code?

I've seen some cases with custom nested types, and can't understand how it works.

These are clear:

struct MyType<T, U> {
   nested: Vec<(T, U)> // almost real-life example
}

let x: Vec<int> = ...
let y: MyType<f32, f32> = ... 

These are not:

 Box::new(GzDecoder::new(fp)?) as Box<dyn Read>
 Box::new(BzDecoder::new(fp)) as Box<dyn Read>
 Result<(), std::io::Error>
 Result<(), Box<dyn Error>>

In Box<T>, can T be a type? (I understand it makes no practical sense, but does the syntax allow this?)

And do I understand correctly that dyn means the next keyword is a trait?

It can only be a type. dyn Trait is a type, in particular.

5 Likes

In Box<T>, T is always a type. dyn Trait is a type.

Yes.

Also, note that generic parameters can only be types, not traits. A trait is not something that you can instantiate and hold onto; it's merely a specification of what methods you can call on a value of an underlying, concrete type. So it's never Generic<Trait>, it's always Generic<Type>.

Traits are put as bounds on generic type parameters, they don't directly act as type parameters. So it's always where SomeParam: Trait and never where SomeParam: Type.

4 Likes

You can read more about trait objects, aka dyn Trait, in the reference.

One key point is that they concrete, static types.

  • They are dynmaically sized, like str and [T] (they do not implement Sized)
  • They perform dynamic dispatch via a vtable
  • But they are not dynamically typed

They do have some special behaviors, but generally, you can think of a dyn Trait as "just" a type that

  • happens to be unsized (so you usually see it in a Box or behind a reference)
  • which you can coerce any (Sized) implementer of Trait into
  • and which also implements Trait itself
1 Like