Why are library trait and library structure incompatible?

Why do I get an error when I try to implement a trait for a structure from the library? Why was it done this way? Wouldn't it be better to remove this restriction?
CleanControl and HttpServer are in the library:

extern crate rust_tcp_sever;
pub use rust_tcp_sever::*;

fn main() { }

impl CleanControl for HttpServer {
    fn work(_stream: &TcpStream) {}
}

And error:

error[E0117]: only traits defined in the current crate can be implemented for types defined outside
of the crate
 --> src\main.rs:6:1
  |
6 | impl CleanControl for HttpServer {
  | ^^^^^^^^^^^^^^^^^^^^^^----------
  | |                     |
  | |                     `rust_tcp_sever::HttpServer` 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

For more information about this error, try `rustc --explain E0117`.

This orphan rule exists to maintain coherence, which is a property guaranteeing that for every type and trait there exist at most one implementation of that trait for that type. Not having this property would lead to various issues, like the ability to create crates that are incompatible due to containing overlapping implementations, making breaking changes easier (due to implementing a trait now becoming a bigger change) and possibly also unsoundness (e.g. some crates rely on some traits not being implemented).

2 Likes

And how can this limitation be circumvented?

Depends on the particulars, but perhaps put HttpServer in your own type (a "newtype") and implement CleanControl for that.

In this specific instance, you can almost certainly solve this by moving the impl CleanControl for HttpServer block into the rust_tcp_server library crate, which I gather to be the crate defined in src/lib.rs of the same project your src/main.rs is in.

A project containing both src/main.rs and src/lib.rs defines two distinct crates, and the orphan rule is applied at the crate level, not the project level.

extern crate rust_tcp_sever;

You should not need this (and should remove it unless you do need it) if you're using the 2018 edition or later.

If you do need it, can you say a bit more about why?

1 Like

Due to the nature of the library, the trait cannot be inserted into the library, but thanks.

And regarding this, unfortunately I don’t know which version of Rust edition to put in Cargo.toml.

Unless you have a specific reason to do otherwise, use the latest edition, which is "2021".

Aha! I misunderstood your error message. Given that, I'd second @quinedot's recommendation to use a newtype to wrap HttpServer, and then implement CleanControl for your new type.

1 Like

Just to double check: what crate defines the CleanControl trait?

The same is true for HttpServer, that is, rust_tcp_sever.

Of all this, only the implementation of trait is needed, so the easiest thing for me is to just create an empty structure.

But thanks for the hint with Rust edition =)

In the docs for that crate, the only trait is CleanSever. Did you add CleanControl but you have not published it yet?

Also, it is very strange that you are implementing a trait for a type, when the trait and the type are in the same library. Usually a library's trait is implemented for a local type, and a value of that local type is passed into the library as a parameter.

Yes, this is the same trait, only the name has been changed to a more laconic one.

This is usually true, but this trait is a feature of the library. I just wanted to try a slightly different type of recording. But this question about this limitation has been bothering me for a long time.

Maybe this part of the crate API design is the root cause of the problem you posted.