Rust中特质的实现冲突问题

28

我希望为&'a str和整数数字(最高可达i32)实现自定义特性,但是Rust不允许我这样做:

use std::convert::Into;

pub trait UiId {
    fn push(&self);
}

impl<'a> UiId for &'a str {
    fn push(&self) {}
}

impl<T: Into<i32>> UiId for T {
    fn push(&self) {}
}

fn main() {}

使用以下错误无法编译:

error[E0119]: conflicting implementations of trait `UiId` for type `&str`:
  --> src/main.rs:11:1
   |
7  | impl<'a> UiId for &'a str {
   | ------------------------- first implementation here
...
11 | impl<T: Into<i32>> UiId for T {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&str`
   |
   = note: upstream crates may add new impl of trait `std::convert::From<&str>` for type `i32` in future versions

&'a str没有实现Into<i32>。是否可以为&'a str和所有能够转换为i32的内容实现UiId,而不指定具体类型?如何做到这一点?


我认为这可能是 Rust 在确定实现是否重叠(或可能重叠)方面的限制,但我还没有找到任何规则明确说明的地方。哎,如果有一个适当的语言规范就好了! - Chris Emerson
2个回答

22

考虑到并没有保证以后不可能添加 &'a str 实现 Into<i32>,所以这个事实并未被考虑在内。如果这样做会导致代码出错。

因此,如果允许这样做,可能会导致库traits难以添加实现。

不幸的是,我找不到相关的文档,无论是在The Rust Programming Language书中还是在参考手册中。

我找到的最好的资料是RFC 1023,它说:一个crate[...]不能依赖于除非Type或Trait为本地类型。


我实际上不认为那是正确的。这不是孤儿规则的用途吗?你不能为外部类型添加外部特性 impl。至少其中一个特性和类型必须在当前 crate 中定义...为了让编译器推理这种情况,我想 - Lukas Kalbertodt
@LukasKalbertodt,请看我的编辑。它在RFC 1023中的孤儿规则的同一句话中。 - starblue
哦,确实,那听起来很合理。谢谢! - Lukas Kalbertodt

12
我找到了一种使用标记特质的解决方法,不需要使用夜间版或实验性功能。这个技巧是我在我的包中定义标记特质并不导出它,因此上游包不能将标记定义在除了我实现的类以外的类上。
在标记特质下面是Numeric。
我使用它来实现Into,以便可以将任何可以转换为f64的东西转换成它,也可以用于字符串和其他类型的单独实现。
Numeric特质必须是pub的,因为他们警告说未来版本将禁止在公共接口中使用私有特质。

use std::convert::Into;

pub trait Numeric {}
impl Numeric for f64 {}
impl Numeric for f32 {}
impl Numeric for i64 {}
impl Numeric for i32 {}
impl Numeric for i16 {}
impl Numeric for i8 {}
impl Numeric for isize {}
impl Numeric for u64 {}
impl Numeric for u32 {}
impl Numeric for u16 {}
impl Numeric for u8 {}
impl Numeric for usize {}


pub trait UiId {
    fn push(&self);
}

impl<'a> UiId for &'a str {
    fn push(&self) {}
}

impl<T: Into<i32> + Numeric> UiId for T {
    fn push(&self) {}
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接