2

In general case, it is not possible to implement in crate C1 a trait defined in C2 for a Type defined in crate C3, being assumed that C1, C2, C3 are different

But is there a trick to designing a trait on purpose, so that such implementations are allowed?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
FreD
  • 393
  • 2
  • 12

1 Answers1

1

Since Rust 1.41, it seems that Rust’s orphan rules have been a little bit more relaxed, as explained in Into doc.

The key is to make the trait dependent on a type parameter which is defined in the implementing crate. For example, the following, crate workspace "orphan", will actually work:

orphan/ | c1/ | src/lib.rs | pub trait MyTrait<T> {
        |     |            |    fn my_task(&self);
        |     |            | }
        |     |
        |     | Cargo.toml | [package]
        |                  | name = "c1"
        |                  | version = "0.0.1"
        |                  | edition = "2021"
        |                  | [dependencies]
        |
        | c2/ | src/lib.rs | pub struct MyStruct;
        |     |
        |     | Cargo.toml | [package]
        |                  | name = "c2"
        |                  | version = "0.0.1"
        |                  | edition = "2021"
        |                  | [dependencies]
        |
        | c3/ | src/lib.rs | use c1::MyTrait; use c2::MyStruct;
        |     |            | pub enum MyT {}
        |     |            | impl MyTrait<MyT> for MyStruct {
        |     |            |    fn my_task(&self) { println!("hello!"); }
        |     |            | }
        |     |
        |     | Cargo.toml | [package]
        |                  | name = "c3"
        |                  | version = "0.0.1"
        |                  | edition = "2021"
        |                  | [dependencies]
        |                  | c1 = { path = "../c1", version = "0.0.1" }
        |                  | c2 = { path = "../c2", version = "0.0.1" }
        |
        | c4/ | src/main.rs | fn main() {
        |     |             |     use c1::MyTrait; use c2::MyStruct;
        |     |             |     #[allow(unused_imports)] use c3;
        |     |             |     MyStruct.my_task();
        |     |             | }
        |     |
        |     | Cargo.toml | [package]
        |                  | name = "c4"
        |                  | version = "0.0.1"
        |                  | edition = "2021"
        |                  | [dependencies]
        |                  | c1 = { path = "../c1", version = "0.0.1" }
        |                  | c2 = { path = "../c2", version = "0.0.1" }
        |                  | c3 = { path = "../c3", version = "0.0.1" }
        |
        | Cargo.toml | [workspace]
                     | members = [ "c1", "c2", "c3", "c4", ]

with result:

cargo run --release
   Compiling c4 v0.0.1 (XXX\orphan\c4)
    Finished release [optimized] target(s) in 0.26s
     Running `target\release\c4.exe`
hello!

In conclusion, there is a trick to design a trait in crate c1, so there is a way in some sense to implement this trait in crate c3 for a type defined in crate c2, assuming c1 , c2, c3 are different!

FreD
  • 393
  • 2
  • 12