请原谅这里的大量代码,但最小的示例很难解释,我已经try 使其尽可能简短.我试图用泛型界限在泛型类型上实现泛型特征,这会导致编译器错误:

note: downstream crates may implement trait `Named<_>` for type `ExampleThing`

奇怪的是,如果命名的函数不接受泛型参数,则可以很好地工作,所以我不明白为什么这会导致问题,以及如何修复它.

Minimally Broken Example

(操场:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=6d0bdb30f1977318a9613b4629c5be80个)

代码的解释可能是阅读注释最容易的.在现实世界中,Named是被引入到Talk<K>make_talk<T, K>的现有代码库中的特征.

use std::fmt::Debug;

/// This trait is to be implemented by downstream crates for objects that talk
/// In order to break things there is a spurious extra piece of information of
/// generic type K that is to be included in the message
pub trait Talks<K> {
    fn say(&self, extra: K);
}

/// A normal use case
pub struct ExampleThing(u64);

/// So we implement it manually like this
impl<K> Talks<K> for ExampleThing where K: Debug {
    fn say(&self, extra: K) {
        println!("I have the number {} [extra: {:?}]", self.0, extra);
    }
}

/// Then this can make any that implemnets Talk, talk.  We can always pass some
/// extra bit of info of type K to include in the message
pub fn make_talk<T, K>(thing: T, extra: K) where T: Talks<K> {
    thing.say(extra);
}

/// A large collection of objects are named, exposing a name method,
/// and they are capable of including the extra info 
pub trait Named<K> {
    fn name(&self, extra: K) -> String;
}

/// For example this guy
pub struct Person{ name: String }

impl<K> Named<K> for Person where K: Debug {
    fn name(&self, extra: K) -> String {
        format!("{:?} of {:?}" , self.name.clone(), extra)
    }
}

/// And then we can try make it so that all things that implement Name
/// introduce themselves automatically using Name
impl<K, T> Talks<K> for T where 
K: Debug,
T: Named<K>
{
    fn say(&self, extra: K) {
        println!("My name is {}", self.name(extra))
    }
}

fn main() { 
    let example = ExampleThing(42);
    make_talk(example, "Did it work?");
    let person = Person{name: "Bob".to_string()};
    make_talk(person, 1234);
}

产生此错误:

error[E0119]: conflicting implementations of trait `Talks<_>` for type `ExampleThing`
  --> src/main.rs:43:1
   |
14 |   impl<K> Talks<K> for ExampleThing where K: Debug {
   |   ------------------------------------------------ first implementation here
...
43 | / impl<K, T> Talks<K> for T where 
44 | | K: Debug,
45 | | T: Named<K>
   | |___________^ conflicting implementation for `ExampleThing`
   |
   = note: downstream crates may implement trait `Named<_>` for type `ExampleThing`

Tiny change that works

如果我们go 掉通用边界Named<K>并将其替换为Named<u64>,那么一切都很好:

impl<K, T> Talks<K> for T where 
K: Debug,
T: Named<u64>
{
    fn say(&self, extra: K) {
        println!("My name is {}", self.name(999))
    }
}

这会产生以下结果:

I have the number 42 [extra: "Did it work?"]
My name is "Bob" of 999

Question

这里发生什么事情?我如何解决这个问题,以便在一揽子实现中将类型为Kextra字段传递到Named<K>中.关于下游 crate 的错误消息令人困惑,因为它仅在性状界限包括K时适用.

推荐答案

如果没有泛型参数,除了您,没有人能为ExampleThing编写Named的实现.这样的实施将使两个隐含impl Talks for ExampleThingimpl<T: Named> Talks for T相互冲突.然而,由于只有您才能编写这个Impl(因为没有其他机箱拥有该类型或特征),编译器只能判断这样的Impl不存在并继续执行.

然而,在Named具有泛型参数的情况下,不仅特征的所有者(Named)或类型的所有者(ExampleThing)可以编写这样的Impl,而且泛型参数的所有者(impl Named<SomeType> for ExampleThing中的SomeType)也可以编写这样的Impl.因此,您不能再确保您的实现不会冲突,这就是编译器抱怨的原因.

您可以将泛型参数移动到方法以解决此问题.

Rust相关问答推荐

文档示例需要导入相关的 struct ,但仅在运行测试时.这是故意的行为吗?

如何在Bevy/Rapier3D中获得碰撞机的计算质量?

当第二个`let`依赖于第一个`let()`时,如何在一行中有多个`let()`?

在UdpSocket上使用sendto时的隐式套接字绑定

在使用#[NO_STD]时,如何在Rust中收到紧急消息?

将一个泛型类型转换为另一个泛型类型

是否提供Bundle 在可执行文件中的warp中的静态文件?

如何限制 GtkColumnView 行数

如何正确使用git2::Remote::push?

如何使用 Bincode 在 Rust 中序列化 Enum,同时保留 Enum 判别式而不是索引?

将多维数组转换为切片

Rust 中的方法调用有什么区别?

Rust/Serde/HTTP:序列化`Option`

哪些特征通过 `Deref` 而哪些不通过?

只有一个字符被读入作为词法分析器的输入

发生移动是因为 `data` 的类型为 `Vec`,它没有实现 `Copy` 特性

你能告诉我如何在 Rust 中使用定时器吗?

如果我不想运行析构函数,如何移出具有析构函数的 struct ?

为什么这个值在上次使用后没有下降?

为什么一个整型变量赋值给另一个变量后仍然可以使用?