问题描述
为什么代码示例 1 可以编译但示例 2 会出现编译错误?
示例 1:
使用 std::ops::Index;结构有界{idx:使用,}impl 索引对于 [i32;4] {类型输出 = i32;fn index(&self, b: Bounded) ->&i32 {不安全 { self.get_unchecked(b.idx) }}}
示例 2:
使用 std::ops::Index;结构有界{idx:使用,}实施<T>索引对于 [T;4] {类型输出 = T;fn index(&self, b: Bounded) ->&T {不安全 { self.get_unchecked(b.idx) }}}
error[E0210]: 类型参数 `T` 必须用作某些本地类型的类型参数(例如 `MyStruct<T>`)-->src/main.rs:7:1|7 |实施<T>索引对于 [T;4] {|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 类型参数`T`必须用作某些局部类型的类型参数|= 注意:只有在当前 crate 中定义的特征才能为类型参数实现
归根结底是有一个很好的理由",但理由并不那么复杂.
问题来了.想象一下我有一个图书馆箱子:
//library.rs酒吧结构狗;酒吧特质说{fn 说话(&self);}
还有两个使用该库 crate 的 crate.
//bark.rs外部板条箱库;impl library::Speak for library::Dog {fn 说话(&self) {println!("woof");}}
//woof.rs外部板条箱库;impl library::Speak for library::Dog {fn 说话(&self) {println!("吠叫");}}
现在,出于某种原因,我想同时使用这两个库:
//main.rs外部板条箱库;extern crate woof;外箱树皮;fn 主(){让雷克斯 = 图书馆::狗;rex.speak();}
这个程序应该输出什么?library::Dog
的 library::Speak
有两个同样有效、无法区分的实现;没有正确的答案.更糟糕的是,如果我最初依赖于 woof
,然后添加 bark
,我的代码将停止编译,或者 - 更糟糕的是 - 开始透明地做错误的事情.有冲突的 trait impls 是一件坏事™.
添加泛型时情况会变得更糟.如果您有:
//barkgeneric.rs外部板条箱库;实施<T>图书馆::为T说话{fn 说话(&self) {println!("woof");}}
//woofgeneric.rs外部板条箱库;实施<T>图书馆::为T说话{fn 说话(&self) {println!("吠叫");}}
您现在有 无限 个相互冲突的特征实现.哎呀.
为了避免这个问题,我们有孤儿规则.孤儿规则的想法是确保任何 impl Trait for Type
都有一个,并且只有一个可以放置的地方.这样,我们就不必担心 impl 冲突;如果孤儿规则设置正确,它们应该是不可能的.
规则归结为:当你impl
一个类型的特征时,特征或类型必须来自当前的箱子.这使得我所有的相互矛盾的例子都不起作用.woof.rs
不能为 library::Dog
实现 library::speak
,因为它们都不是来自它的箱子.>
同样,你不能impl;索引对于 [T;4];
,因为[T;4]
不是来自你的 crate,并且 rustc
已经决定 Index
也不算来自你的 crate.
但是,它确实让您的 impl Index;对于 [i32;4]
通过,因为在这种情况下 Index
确实来自您.这可能是一个错误,但也可能只是预期的行为;孤儿规则比我在这里所说的要复杂一些,而且它们可能以奇怪的方式相互作用.
更多细节参见rustc --explain E0117
, rustc --explain E0210
.
Why does code example 1 compile but example 2 gives a compilation error?
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[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
It does boil down to "there's a good reason", but the good reason isn't all that complicated.
Here's the problem. Imagine I've got a library crate:
// library.rs
pub struct Dog;
pub trait Speak {
fn speak(&self);
}
And two crates that use that library 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");
}
}
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 isn't a right answer. What's worse, if I depended on woof
originally, and added bark
later, my code would stop compiling, or - worse - start transparently doing the wrong thing. Conflicting trait impls are a Bad Thing™.
It gets worse when you add generics. 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 trait impls. Whoops.
To avoid this problem, we have the orphan rules. The idea of the orphan rules is to make sure that any impl Trait for Type
has one, and only one, place it can be put. That way, we don't have to worry about impl conflicts; they should be straight-up impossible, if the orphan rules are set up correctly.
The rules boil down to: when you impl
a trait for a type, either the trait or the type has to come from the current crate. This makes all of my conflicting examples not work. woof.rs
can't implement library::speak
for library::Dog
, because it neither of them come from its crate.
Similarly, you can't impl<T> Index<Bounded> for [T; 4];
, because [T; 4]
doesn't come from your crate, and rustc
has decided that Index<Bounded>
doesn't count as coming from your crate either.
It does, however, let your impl Index<Bounded> for [i32; 4]
through, because in this case Index<Bounded>
does come from you. It's possible that's a bug, but it's also possible that it's just intended behavior; the orphan rules are slightly more complex than what I've stated here, and they might be interacting in weird ways.
For more specifics, see rustc --explain E0117
, rustc --explain E0210
.
这篇关于为什么一致性规则会引发错误“类型参数必须用作某些本地类型的类型参数"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!