I know the orphan rule but why is this working with Box then? If change the pointer to be a Box this compiles just fine? The orphan rule doesn't apply to Box?
P.S. (This is a minimal example, in my real program I need it to be an Arc. Or it could be a Box but I would need to have a way to convert it to Arc which doesn't seem possible? )
use std::str::FromStr;
use std::sync::Arc;
trait Pet {
}
struct Cat();
struct Dog();
impl Pet for Cat{}
impl Pet for Dog{}
impl FromStr for Arc<dyn Pet> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"cat" => Ok(Arc::new(Cat{})),
"dog" => Ok(Arc::new(Dog{})),
_ => Err(())
}
}
}
fn main() {
let pet = "cat".parse::<Arc<dyn Pet>>();
}
Compiling playground v0.0.1 (/playground)
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src/main.rs:14:1
|
14 | impl FromStr for Arc<dyn Pet> {
| ^^^^^^^^^^^^^^^^^------------
| | |
| | `std::sync::Arc` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
error: aborting due to previous error
For more information about this error, try `rustc --explain E0117`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
This isn't directly related to your question around coherence, but it feels a little weird to be attaching new trait implementations (FromStr in this case) to a trait object. I feel most people would implement this as a free function (e.g. fn parse_pet(s: &str) -> Result<Arc<dyn Pet>, Error>) instead.
Free functions tend to be more discoverable than trait implementations because they appear in the API docs for a module instead of being buried at the bottom of the Pet trait's page. From what I've seen trait objects tend to be used primarily to allow dynamic dispatch on the trait they represent, and other than anomalies like Any::downcast() you don't often see them having their own methods or trait impls.
Of course, this is just convention and personal preference. It may make perfect sense to implement FromStr in your use case.
If it’s not enough just to have trait Trait : Debug and have Arc use the wrapped trait’s implementation, then I think I’d write a wrapper type with one field that would have its own implementations of things like FromStr and Debug, possibly with a Deref that delegated to Arc’s Deref.