Why do coherence rules cause the error “type parameter should be used as a type parameter for some local type”?

Can you explain why code compiler 1 compiles, but example 2 gives a compilation error? If the explanation is too long and complicated (since I suspect that I am explaining the rules of consistency, perhaps I would agree with a simple answer like "Believe me, there is a good reason why the language forbids the code in Example 2".

Example 1:

use std::ops::Index; struct Bounded { idx: usize } impl Index<Bounded> for [i32; 4] { type Output = i32; fn index(&self, b: Bounded) -> &i32 { unsafe { self.get_unchecked(b.idx) } } } 

Example 2:

 use std::ops::Index; struct Bounded { idx: usize } impl<T> Index<Bounded> for [T; 4] { type Output = T; fn index(&self, b: Bounded) -> &T { unsafe { self.get_unchecked(b.idx) } } } // error: type parameter `T` must be used as the type parameter for // some local type (eg `MyStruct<T>`); only traits defined in the // current crate can be implemented for a type parameter [--explain E0210] 
+5
source share
1 answer

It comes down to "there is a good reason," but a good reason is not so complicated.

Here is the problem. Imagine I have a library box:

 // library.rs pub struct Dog; pub trait Speak { fn speak(&self); } 

And two drawers that use this library drawer.

 // bark.rs extern crate library; impl library::Speak for library::Dog { fn speak(&self) { println!("woof"); } } 
 // woof.rs extern crate library; impl library::Speak for library::Dog { fn speak(&self) { println!("bark"); } } 

Now for some reason I want to use both of these libraries:

 // main.rs extern crate library; extern crate woof; extern crate bark; fn main() { let rex = library::Dog; rex.speak(); } 

What should this program output? There are two equally valid, indistinguishable implementations of library::Speak for library::Dog ; there is no right answer. What's worse, if I used to depend on woof and added bark later, my code stopped compiling or, even worse, it became transparent to do the wrong thing. Conflicting traits are Bad Thing ™.

Amplified when generics are added. If you have:

 // barkgeneric.rs extern crate library; impl<T> library::Speak for T { fn speak(&self) { println!("woof"); } } 
 // woofgeneric.rs extern crate library; impl<T> library::Speak for T { fn speak(&self) { println!("bark"); } } 

You now have an infinite number of conflicting attributes. Oops

To avoid this problem, we have orphaned rules. The idea of ​​orphaned rules is to make sure that any impl Trait for Type has one, and only one, place can be placed. Thus, we do not need to worry about conflicts with conflicts; they should be straightforward if spelling rules are set correctly.

The rules come down to: when you impl attribute for a type, either a trait or a type must come from the current box. This makes all my conflicting examples inoperative. woof.rs cannot implement library::Speak for library::Dog , because none of them come from their mailbox.

Similarly, you cannot impl<T> Index<Bounded> for [T; 4]; impl<T> Index<Bounded> for [T; 4]; because [T; 4] [T; 4] does not come from your mailbox, but rustc decided that Index<Bounded> does not count as outgoing from your mailbox.

However, let your impl Index<Bounded> for [i32; 4] impl Index<Bounded> for [i32; 4] through, because in this case the Index<Bounded> comes from you. It is possible that it is a mistake, but it is also possible that it is simply intended for behavior; The rules for orphans are a little more complicated than what I said here, and they can interact in strange ways.

See rustc --explain E0117 , rustc --explain E0210 .

+3
source

All Articles