Why is Rc<dyn T> a foreign type when Box<dyn T> isn't?

This compiles

trait T {}
struct S;

impl From<i32> for Box<dyn T> {
    fn from(_: i32) -> Self {
        todo!()
    }
}

But this doesn't, with an error that you can't implement a foreign trait for a foreign type. But the trait object is a local type, and the only difference in this code from the above is using Rc instead of Box

use std::rc::Rc;

trait T {}
struct S;

impl From<i32> for Rc<dyn T> {
    fn from(_: i32) -> Self {
        todo!()
    }
}

Is this intended? Why?

Box is special.

Box is considered "fundamental" (and the compiler knows about it as part of the language, rather than just the library) which allows this. The same is true of references (& and &mut). Rc is just, I believe, defined at the library level so it doesn't permit such trait impls.

It's actually not that the Box is special (although it is special indeed), it's just explicitly marked as #[fundamental]. Rc, on the other hand, is not. I don't know the reasons - it's probably just due to the fact that removing this "fundamental" marker is a breaking change, and therefore standard library is a bit too cautious.

3 Likes

#[fundamental] is a big exception to the orphan rules, and was created to not break existing implementations or negative reasoning about references, Box, and the Fn traits specifically (which I consider pretty special).

I don't think I'd call being very conservative with the attribute too cautious either. And it's not just about removing it later. Making a type #[fundamental] makes it a breaking change for the type owner to have any new blanket trait implementations (unless implementing a trait which is at the same time new). For example, this would have been a breaking change because if Rc was fundamental, I might have had a impl AsFd for Rc<dyn MyTrait>.

3 Likes