My understanding of the orphan rule of interest is that:
- For any
implof a Trait on a Type, either the Trait or the Type must be defined in the same crate as theimpl.
or equivalently:
- It is impossible to implement a trait defined in a foreign crate on a type which is also defined in a foreign crate.
So the question is, how should one design a library crate that provides a type which should implement a foreign trait, but leave it to the consumer crate to define the implementation of that trait?
For example, imagine a hypothetical library crate, cards, which provides types representing cards in a standard 52-card deck of playing cards intended for use in external crates to facilitate the development of card game programs. Such a library might provide the following type:
/// Ranks of a standard French-suited playing card
pub enum Rank {
Ace,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
}
Since a substantial portion of popular card games involve comparing the ranks of 2 cards, it should make sense for enum Rank to implement std::cmp::PartialOrd so that ranks can easily be compared with the <, >, <=, and >= operators. But crate cards should not define this implementation because the ranks of playing cards are not intrinsically equipped with a partial ordering relation; such a relation is instead optionally imposed on them by the specific game within which the cards are being used and in general, the implementation varies by game. For example, some games consider Ace to be greater than all other ranks while others consider Ace to be less than all other ranks and certain games may not even compare ranks at all and therefor not need a PartialOrd implementation.
In keeping with best practice regarding separation of concerns, it seems obvious that crate cards should not implement PartialOrd, but instead leave its implementation up to the consumers of cards, allowing each consumer to define their own partial ordering relation for the ranks. However, since in a crate depending on cards, both Rank and PartialOrd are crate-foreign, this is prohibited by the Orphan rule.
What is the best way to deal with this scenario? It seems to me the only correct thing to do is to refrain from implementing PartialOrd for Rank and let the consumers make their own functions for rank comparison like:
fn blackjack_rank_compare(first: &Rank, second: &Rank) -> Option<Ordering> {...}
fn poker_rank_compare(first: &Rank, second: &Rank) -> Option<Ordering> {...}
fn war_rank_compare(first: &Rank, second: &Rank) -> Option<Ordering> {...}
// etc.
However this is inconvenient. Is there any viable workaround for this problem?
There are a lot of answers to your question.
You could for example use a type parameter for defining the ordering (playground):
which results in:
In this case, the user of your crate is able to change the order of your cards by defining a type that implements MyOrder.
But have I well understood your question?