If you want to express something like "this is a variable that holds something that fulfils this trait", without knowing the _actual_ type it is, that variable effectively has an unknown runtime size. std::io::Read is an interface for reading bytes of some source, like a file or a socket.
This matters because we're talking about a stack frame. So the size needs to be known at compile time.
let a: u64 = 42; // ok, because well known size.
let b: Read = ...; // illegal, because unknown size.
A "trait object" places the object on the heap and has a pointer in its place. let b: Box<Read> = ... // legal, because pointer is a known size
However it's a bit more complicated, because this syntax allows for dynamic dispatch at runtime using a vtable. So there's a quite big difference between Box<u64> (a 64 bit unsigned integer on the heap) vs Box<Read> (a runtime dispatched lookup via a vtable).This difference is not obvious at a glance though. Hence the new syntax: Box<dyn Read>.
(I think I got that right)