and try to impliment the trait on a struct from another crate I imported called Player
impl Score for Player {
...
}
But I get a message that I can't do that because the trait must be local.
What is the best approach to create a modular library that you can import and implement features across the different crates? or am I trying to do the impossible here?
There are only two crates that can impl Score for Player: the one that defines Score and the one that defines Player. If any other crate could, then more than one crate could, which would create ambiguity about which implementation should be used.
This does sometimes interfere with modularity, but it prevents situations where a program could never use two different crates together without a conflict.
It's possible for a third crate to introduce its own wrapper type:
struct MyPlayer(Player);
impl Score for MyPlayer {}
And that is sometimes the best option, but probably is not if you are designing all the involved crates yourself.
You should put the impl Score for Player in either Score's crate or Player's crate and have one depend on the other.
I wouldn't phrase it as "doing the impossible" (although you technically are, because of the orphan rules), instead I would say that this is a code smell and what you are trying to do won't actually make things more "modular" - all it does is move code around.
Let's think of the semantics of what you are trying to do from an architecture/design perspective... We've got a type Player and a trait Score, and somehow some third, unrelated crate is trying to say how Player implements Score. Doesn't that go against encapsulation and the single responsibility principle? Shouldn't only the Player type or Score trait know how impl Player for Score is implemented?