Rust does not allow dynamic trait methods to have type parameters, but some of our attribute functions need to be generic.
We worked around this by adding a Self: Sized type bound.
This lets the trait still be usable as a type, but means we don't have access to the functions from the trait.
For example:
let x: &dyn Attributable = ...;
x.has_attribute(...);
will not work. Even though has_attribute is defined in Attributable, it isn't reachable because of the Self: Sized.
Same for Entity, and all the other traits that involve Attributable.
In these places, instead of being able to use the function, we copy/paste the function's implementation instead. This is not great.
This PR gives us the best of both worlds by splitting the Attributable trait into 2 traits:
Attributable: This only contains the non-generic functions. So it's just a normal trait.
AttributeFunctions which only contains the generic functions. But without the old Self: Sized.
Without this bound, traits can access the functions declared on it, but we can't use &dyn AttributeFunctions as a type.
But this is fine, we only use &Attributable, and this new trait only exists in the background to make everything work.
Finally, we blanket implement AttributeFunctions for Attributable.
This means anything that implements Attributable can use these functions, but without restricting the type of Attributable to only Sized types.
TL;DR
Look at the last line of this PR's changes. We can use the attribute functions like normal now.
Rust does not allow dynamic trait methods to have type parameters, but some of our attribute functions need to be generic.
We worked around this by adding a
Self: Sized
type bound. This lets the trait still be usable as a type, but means we don't have access to the functions from the trait. For example:will not work. Even though
has_attribute
is defined inAttributable
, it isn't reachable because of theSelf: Sized
. Same forEntity
, and all the other traits that involveAttributable
.In these places, instead of being able to use the function, we copy/paste the function's implementation instead. This is not great.
This PR gives us the best of both worlds by splitting the
Attributable
trait into 2 traits:Attributable
: This only contains the non-generic functions. So it's just a normal trait.AttributeFunctions
which only contains the generic functions. But without the oldSelf: Sized
. Without this bound, traits can access the functions declared on it, but we can't use&dyn AttributeFunctions
as a type. But this is fine, we only use&Attributable
, and this new trait only exists in the background to make everything work.Finally, we blanket implement
AttributeFunctions
forAttributable
. This means anything that implementsAttributable
can use these functions, but without restricting the type ofAttributable
to onlySized
types.TL;DR
Look at the last line of this PR's changes. We can use the attribute functions like normal now.