Impl foreign trait for a foreign struct (I control all traits and structs)

i have a rather basic question. I have

              +----my lib crate1 [ pub struct A<U> {} ]
              |
              |
my bin crate--|
              |
              |
              +----my lib crate2 [ pub trait Tr {} ]

in lib crate1 I do

impl Tr for A<Type1> { ... }

but in binary crate I need to do impl Tr for A<Type2>, and I cannot, since it won't allow me to impl foreign trait for a foreign struct (impl for type defined outside of crate). Is there a way around this?

I understand why such restriction exists for library crates, but not for binary crates, since nothing else can depend on them.

What if crate2 at some time added the dependency on crate1 and implemented Tr for A<Type2>? This would be breaking change, if your attempt was allowed.

2 Likes

I was reading the rust book yesterday and read about the newtype pattern. Wouldn't that work here?

here's an example of implementing Display on Vec

src/main.rs
use std::fmt;

struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

fn main() {
    let w = Wrapper(vec![String::from("hello"), String::from("world")]);
    println!("w = {}", w);
}

We’re allowed to implement a trait on a type as long as either the trait or the type are local to our crate. It’s possible to get around this restriction using the newtype pattern, which involves creating a new type in a tuple struct. (We covered tuple structs in “Using Tuple Structs Without Named Fields to Create Different Types” on page 86.) The tuple struct will have one field and be a thin wrapper around the type we want to implement a trait for. Then the wrapper type is local to our crate, and we can implement the trait on the wrapper.

That's true, but it's common enough for code in a binary to be moved at some point to a library, for example when you want to add a second binary. And making some code work or not work depending on the crate type could be a point of confusion.

1 Like

Given the kinds of questions I see here often enough, I can pretty much guarantee that it would be confusing to especially newcomers. But as a seasoned Rustacean, I wouldn't find that particularly welcome either, as it would randomly cause my productivity to drop while trying to figure out why a crate that used to build and that I haven't made any changes to won't build anymore.

1 Like

That's the rationale, thanks.

I ended up doing this, but then I have to use ugly .0 references everywhere to the content of the tuple struct. I wonder why Rust does not have struct embedding, like Go?

I think you can try implementing deref on your new type, so you can directly access the internal type's methods and stuff without the .0 everywhere. I'm sure it'll come with all sorts of limitations too, but better than nothing.

2 Likes

If you implement Deref<Target=Vec<String>> or whatever your interior type is ...

You beat me to it

1 Like

So I looked up Go's struct embedding (I do know go fairly well, even used it professionally to a minimal level. But never heard of struct embedding before lol)

With deref it seems like the behavior would be close to being similar to that right?

I impl'ed Deref, but ran into a problem with the borrow checker: one of the inner struct members itself contains references and gets mutated. It therefore required implementing DerefMut and that led to the changes in the traits (beyond adding .0 )that I did not want, so for now I left it with .0 access.

No, ypu don't. Just switch from a tuple struct to a regular struct and give the field a name. Then you can just use that instead of .0.

In terms of performance or memory layouts it shouldn't matter anyway.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.