为什么一致性规则会引发错误"类型参数必须用作某些本地类型的类型参数"?

kmk*_*mky 5 rust

为什么代码示例1编译但示例2给出了编译错误?

例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) }
    }
}
Run Code Online (Sandbox Code Playgroud)

例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) }
    }
}
Run Code Online (Sandbox Code Playgroud)
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
 --> src/main.rs:7:1
  |
7 | impl<T> Index<Bounded> for [T; 4] {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `T` must be used as the type parameter for some local type
  |
  = note: only traits defined in the current crate can be implemented for a type parameter
Run Code Online (Sandbox Code Playgroud)

Jam*_*les 5

它确实可以归结为“有一个充分的理由”,但充分的理由并不那么复杂。

\n\n

问题就在这里。想象一下我有一个图书馆箱子:

\n\n
// library.rs\npub struct Dog;\npub trait Speak {\n    fn speak(&self);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

还有两个使用该库板条箱的板条箱。

\n\n
// bark.rs\nextern crate library;\nimpl library::Speak for library::Dog {\n    fn speak(&self) {\n        println!("woof");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\n
// woof.rs\nextern crate library;\nimpl library::Speak for library::Dog {\n    fn speak(&self) {\n        println!("bark");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,出于某种原因,我想使用这两个库:

\n\n
// main.rs\nextern crate library;\nextern crate woof;\nextern crate bark;\n\nfn main() {\n   let rex = library::Dog;\n   rex.speak();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个程序应该输出什么?library::Speakfor有两个同样有效、无法区分的实现library::Dog;没有正确的答案。更糟糕的是,如果我woof原来依赖,并添加bark后来添加,我的代码将停止编译,或者 - 更糟糕 - 开始透明地做错误的事情。冲突的特征实现是一件坏事\xe2\x84\xa2。

\n\n

当你添加泛型时,情况会变得更糟。如果你有:

\n\n
// barkgeneric.rs\nextern crate library;\nimpl<T> library::Speak for T {\n    fn speak(&self) {\n        println!("woof");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\n
// woofgeneric.rs\nextern crate library;\nimpl<T> library::Speak for T {\n    fn speak(&self) {\n        println!("bark");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

你现在拥有无限个相互冲突的特质实现。哎呀。

\n\n

为了避免这个问题,我们有孤儿规则。孤儿规则的想法是确保任何impl Trait for Type都有一个且只有一个可以放置的位置。这样,我们就不必担心 impl 冲突;如果孤儿规则设置正确的话,它们应该是完全不可能的。

\n\n

这些规则归结为:当您impl为某个类型添加特征时,该特征或该类型必须来自当前的板条箱。这使得我所有相互矛盾的例子都不起作用。woof.rs无法library::speak实现library::Dog,因为它们都不是来自它的板条箱。

\n\n

同样,您也不能impl<T> Index<Bounded> for [T; 4];,因为它[T; 4]不是来自您的板条箱,并且rustc已决定Index<Bounded>也不算是来自您的板条箱。

\n\n

然而,它确实让你impl Index<Bounded> for [i32; 4]通过,因为在这种情况下Index<Bounded>确实来自你。这可能是一个错误,但也可能只是预期的行为;孤儿规则比我在这里所说的稍微复杂一些,并且它们可能以奇怪的方式相互作用。

\n\n

有关更多详细信息,请参阅rustc --explain E0117rustc --explain E0210

\n