@jarkko First and foremost there are only traits. If they are then used as generic trait bounds or dynamic (vtable) pointer is another thing. There are some semantic differences. Not every trait is dyn-compatible/object safe. dyn is only available behind some kind of (fat-)pointer, be it a reference or some kind of box. While generics will be a single concrete type (at compile time).
For 'dyn' you pay a (often negligible) runtime cost plus sometimes a boxing cost. Generics don't but when you overuse them they bloat the binary and you pay with cache misses. This means the decision is often dictated by usage and intended semantics.
@jarkko The conclusion is that the intended usage dictates how you use traits. Often you can not choose arbitrarily if it shall be generic or dyn. But when you do the wrong thing the compiler will nudge you.
It is not the trait that is different dynamic/generic, but how you use it:
trait Trait {}
// the T is part of the type you can instantiate this with different T's, but for each different T you get a new type. The T is used directly not over a pointer
// You can access T's methods as well as any trait methods
struct Generic<T: Trait>(t:T);
// here you have only one single dynamic type, you can instantiate it with anything that implements Trait, but only the Trait methods are available. 'dyn' also mandates (as i saied before) a (fat-)pointer, here that is a Box, but references or other things are possible too.
struct Dynamic(Box<dyn Trait>);
Thats just the surface, there are more differences, but it is the usage that differentiates generic vs dyn, not the Trait
You where not wrong saying that using Traits in generic context is similar to macros as it expands the code (you may try optimization/LTO to merge that back again) monomorphizing generics can (and will) bloat and increase compile times. While dyn leads to leaner binaries at a little runtime cost.
I just wanted to point out that there are semantic differences in the language. Using a trait generic vs dynamic is sometimes not a choice so there is no general rule what you should use by default.
If you like you can look at my univec crate for reference:
https://docs.rs/univec/1.1.0/univec/
which does some mix/translation between generics and dyn (Any)