Test case:
a.rs
pub trait Foo {}
b.rs
extern crate a;
struct Bar;
impl a::Foo for Vec<Bar> {}
fn main() {}
In rustc 1.1.0-nightly (90cc83015 2015-04-22) (built 2015-04-23):
$ rustc --crate-type lib a.rs
$ rustc -L . a.rs
a.rs:3:1: 3:28 error: the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types [E0117]
a.rs:3 impl b::Foo for Vec<Bar> {}
^~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Note _the impl does not reference any types defined in this crate_. This is not true, Bar
is defined is this crate.
This indicates a bug either in the coherence rules (this program should be valid and the error not be shown) or in the error message, which is misleading.
CC @nikomatsakis
In either case, the work-around / fix is to make a new type that wraps Vec<Bar>
and implement the trait on that.
I think this is an intentional result of #23086 (RFC 1023).
I think the rationale is so that Vec<_> could be made to implement the trait later on without breaking backwards compatibility (it would conflict with your implementation of the trait otherwise).
@Diggsey by “later on”, do you mean “in a future version of the crate that defines Vec
?” Ok, that’s understandable. In that case, this issue is about reformulating the error message.
This error now provides a link to a detailed explanation of E0117 so this issue might be considered fixed?
what's the reason for this restriction in the 1st place?
An example use case for this:
I have a library that implements an AST, Parser and Serializer for a given DSL. I'd like to keep it lean because that's what people will bundle.
I want to have a separate crate that adds serde and allows to serialize and deserialize the AST to/from json.
I think that this limitation prevents me from being able to do that.
If this is allowed, adding impls
s become a breaking-change, because any other crate could potentially have an conflicting impl. We need to bump minor version (or major version if >1.0.0
) of a crate for every addition of trait impl. Moreover, as standard library is generally not allowed to make breaking-change, we can't implement existing std traits for existing std types anymore.
if your concern is the additional dependencies, you can use features.
Sure, features are one option, but if I don't want to mess with the original package and keep it clean, I just end up with something like this:
which is a clone of https://github.com/projectfluent/fluent-rs/blob/70b5e8d387f3b836032b42c997c359263b21c8d0/src/syntax/ast.rs
but with From/Into. (to serialize FTL->AST->JSON and JSON->AST->FTL back).
I'm wondering if it would be possible to write code to automate the creation of a clone of a struct/enum from another crate. Then one could add Serialize/Deserialize on those local struct/enums.
Another option would be to mark structs/enums as extensible by other crates, which would allow the author to decide that he wants to let others impl features for them. That would work better in my case, because it would remove the step of converting from fluent::syntax::ast::* into fluent_json::ast::* back and forth.
I think the rationale is so that Vec<_> could be made to implement the trait later on without breaking backwards compatibility (it would conflict with your implementation of the trait otherwise).
With specialization this is not an issue anymore. If some library implements Foo for Vec<T>
and I implement Foo for Vec<Bar>
then with specialization both can co-exist and mine will be executed. Therefore I think that allowing implementations of traits for a type not defined in your crate but with a generic parameter from your trait could be allowed again. Do I miss another problem?
This indicates a bug either in the coherence rules (this program should be valid and the error not be shown) or in the error message, which is misleading.
The bug is in the error message, which I agree is misleading. It's also true that it might be nice to rejigger the rules in this area, but that's a fairly complex undertaking.
Here is a standalone test case for the same issue:
use std::fmt;
struct Bar;
impl fmt::Display for Vec<Bar> { }
fn main() { }
Generates:
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> <anon>:3:1
|
3 | impl fmt::Display for Vec<Bar> { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate
|
= note: the impl does not reference any types defined in this crate
= note: define and implement a trait or new type instead
I'm not sure of the ideal wording, but one idea would be to have it highlight the "foreign types" (in this example, there is only one, the Vec
type):
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> <anon>:3:1
|
3 | impl fmt::Display for Vec<Bar> { }
| ^^^^ not defined in the current crate
|
= note: define and implement a trait or new type instead
I am not sure if that last note is all that useful either. Maybe. It seems very jargon heavy.
Anyway, the trick would be getting the highlight to exclude the Bar
. It may also be worth trying to comment on how Bar
doesn't count because it is "under" Vec
.
Would it make sense to consider type parameters' boundaries to allow anchoring / scoping impls for non-local types? e.g.
use std::fmt;
struct Bar;
trait LocalMarker {}
impl LocalMarker for Bar {}
impl<T: LocalMarker> fmt::Display for Vec<T> { }
fn main() { }
The current output for this case is:
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src/main.rs:3:1
|
3 | impl fmt::Display for Vec<Bar> { }
| ^^^^^^^^^^^^^^^^^^^^^^--------
| | |
| | `std::vec::Vec` 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 the last comment:
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src/main.rs:5:1
|
5 | impl<T: LocalMarker> fmt::Display for Vec<T> { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------
| | |
| | `std::vec::Vec` 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 the original report:
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> b.rs:3:1
|
3 | impl a::Foo for Vec<Bar> {}
| ^^^^^^^^^^^^^^^^--------
| | |
| | `std::vec::Vec` 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
@nikomatsakis is there anything else we should do here?
I know what the issue is and what the fix is, but I don't understand what "define and implement a trait or new type instead" is trying to tell me.
"define and implement a trait or new type" should presumably be "define and implement a new trait, or implement the trait on a newtype". As it's written the suggestion doesn't make sense.
Even with that alternative wording, it implies either option is feasible, but realistically creating a brand new trait is unlikely to be what the user wants. It might be worth only mentioning the newtype option, or at least listing it first.
"note: define ..." should probably be "note: consider defining ...", because actually creating a newtype or new trait may not fit with the rest of the user's program. Just "define ... instead" implies the compiler's suggestion is a drop-in replacement.
So with all that, should it instead be "consider defining a newtype and implement the trait on it instead" ?
The original test case now compiles, I believe this was fixed with https://blog.rust-lang.org/2020/01/30/Rust-1.41.0.html#relaxed-restrictions-when-implementing-traits
Most helpful comment
Would it make sense to consider type parameters' boundaries to allow anchoring / scoping impls for non-local types? e.g.