Use marker trait pattern to simplify component delegation #12
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR introduces a number of breaking changes to make delegation of group of components simpler.
New syntax for
delegate_components
Old syntax:
New syntax:
Component Marker Trait
A component marker trait is used to mark whether a component can be delegated to a component graph. The
#[mark_component(MarkerName)]
syntax can be used to create a component trait for a specific component graph.For example, the following code:
would be desugared to contain the marker trait definition:
Delegate All
The marker trait can be used for auto component delegation using the new
delegate_all!
macro.For example, with the following code:
The call to
delegate_all!
would be expanded into the following blanket implementation:In this way,
MyExtendedComponents
would automatically delegate bothFooComponent
andBarComponent
toMyComponents
without having explicitly list out the components.It is worth noting that the blanket implementation of
DelegateComponent
can only be done once. So it is not possible to usedelegate_all!
multiple times to delegate to multiple non-overlapping component graphs.The additional use of
Self: IsMyComponent<Component>
instead ofComponent: IsMyComponent
is necessary to workaround the limitation that Rust places on conflicting trait implementation.Consider the alternative naive implementation of the desugared code:
If we use
NaiveIsMyComponent
in a separate crate as follows:We would get a compile error saying
conflicting implementations of trait [...] note: upstream crates may add a new impl of trait in future versions
.The conflict is because the crate for
my_extended_components
does not own both theNaiveIsMyComponent
trait, and the genericComponent
type that is used asSelf
inMyComponents
. As described in rust-lang/rfcs#2758, this is an artificial limitation placed by Rust to ensure that adding new trait implementation to aSelf
type will not introduce breaking changes to downstream crates.We found a workaround in this PR. The trick is that the limitation can be lifted as long as the crate owns the
Self
type. What we need is for the provider of the marker trait to provide a blanket implementation of all possible self types:This way, when we use the constraint
Self: IsMyComponent<Component>
, Rust becomes happy to accept the constraint and no longer prevent future breaking changes. We can verify that the original limitation still exists if we changeSelf
to other types that the crate do not own, such as(): IsMyComponent<Component>
.Delegation Marker Trait
As a complement to
delegate_all!
, the#[mark_delegate(DelegateMarker)]
syntax can be used to auto implement a marker trait that indicates that another component graph has delegated all component to the target component graph.For example, the following code:
would include the following code expansion:
The trait
DelegatesToMyComponents
would automatically be implemented by other component graphs that usedelegate_all!
withMyComponents
, such asMyExtendedComponents
.The delegation marker trait can be used to reason about the property of generic contexts that make use of a specific component graph. For example, we can construct a constraint closure around any context with component graph that delegates to
MyComponents
.Further Details
Further details will be written, once I have the time to write down the detailed documentation for context-generic programming.