【问题标题】:Why do the coherence rules raise the error "the type parameter must be used as the type parameter for some local type"?为什么连贯性规则会引发错误“类型参数必须用作某些本地类型的类型参数”?
【发布时间】:2016-06-29 17:41:23
【问题描述】:

为什么代码示例 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) }
    }
}

示例 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[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

【问题讨论】:

  • 这里的问题实际上是孤儿规则,而不是连贯规则:)。可以通过rustc --explain E0210查看说明。
  • @kennytm:这不应该是一个答案(需要详细说明)吗?

标签: rust


【解决方案1】:

这确实归结为“有充分的理由”,但充分的理由并不那么复杂。

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

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

还有两个使用该库的 crate。

// 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");
    }
}

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

// main.rs
extern crate library;
extern crate woof;
extern crate bark;

fn main() {
   let rex = library::Dog;
   rex.speak();
}

这个程序应该输出什么?对于library::Doglibrary::Speak 有两个同样有效的、无法区分的实现;没有正确的答案。更糟糕的是,如果我最初依赖 woof,后来添加了 bark,我的代码将停止编译,或者 - 更糟糕的是 - 开始透明地做错事。冲突的 trait impl 是一件坏事™。

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

// 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");
    }
}

您现在有 无限 个相互冲突的 trait impl。哎呀。

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

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

同样,您不能impl&lt;T&gt; Index&lt;Bounded&gt; for [T; 4];,因为[T; 4] 不是来自您的箱子,而且rustc 已决定Index&lt;Bounded&gt; 也不算来自您的箱子。

但是,它确实让您的impl Index&lt;Bounded&gt; for [i32; 4] 通过,因为在这种情况下,Index&lt;Bounded&gt; 确实来自您。这可能是一个错误,但也有可能只是预期的行为;孤儿规则比我在这里所说的稍微复杂一些,它们可能会以奇怪的方式进行交互。

更多详情,请参阅rustc --explain E0117rustc --explain E0210

【讨论】:

    猜你喜欢
    • 2016-05-23
    • 1970-01-01
    • 2016-09-30
    • 1970-01-01
    • 2020-03-06
    • 2023-01-17
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    相关资源
    最近更新 更多