rust 为什么我们需要一个单独的impl来实现一个超特征

vngu2lb8  于 2023-03-30  发布在  其他
关注(0)|答案(2)|浏览(133)

阅读Rust书中高级特质章节中关于超级特质的部分。试图将其应用于一个超级简单的具体示例中,有一些我不明白的地方。
当我为一些自定义trait定义trait绑定/supertrait时,我被迫在一个单独的impl块中为一个特定的结构体编写该supertrait的实现。
此示例工作:

use std::fmt::{Display, Formatter};

pub trait Person: Display {
    fn say_hello(&self);
}

pub struct Student {
    name: &'static str
}

impl Person for Student {
    fn say_hello(&self) {
        println!("Hi, i am {}", self)
    }
}

impl Display for Student {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        return write!(f, "{}", self.name);
    }
}

但是,如果我尝试将fmt实现移动到impl Person块中,它就会失败:

...

impl Person for Student {
    fn say_hello(&self) {
        println!("Hi, i am {}", self)
    }
    
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        return write!(f, "{}", self.name);
    }
}

编译器抱怨:

error[E0407]: method `fmt` is not a member of trait `Person`
  --> src/supertrait_question.rs:16:5
   |
16 | /     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
17 | |         return write!(f, "{}", self.name);
18 | |     }
   | |_____^ not a member of trait `Person`

error[E0277]: `Student` doesn't implement `std::fmt::Display`
  --> src/supertrait_question.rs:11:6
   |
11 | impl Person for Student {
   |      ^^^^^^ `Student` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `Student`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
note: required by a bound in `Person`
  --> src/supertrait_question.rs:3:19
   |
3  | pub trait Person: Display {
   |                   ^^^^^^^ required by this bound in `Person`

我的问题是为什么?
因为我定义了DisplayPerson的supertrait,所以Person trait的实现需要Display trait也被实现。trait只是一个collection of methods,所以我希望Person trait的所有方法都可以在同一个块中实现。
语句 “method fmt is not a member of trait Person 看起来很奇怪,因为Person绑定了Display trait,这意味着fmtPerson的成员。
这可以通过简单地完全 * 省略 * impl Display for Student块来进一步证明,这会导致编译器告诉我 “trait std::fmt::Display is not implemented for Student。因此不可能实现Person,但实现Display
我知道答案可能只是 “这就是Rust的工作方式”,但我想知道原因。

u3r8eeie

u3r8eeie1#

一个声明了supertraits的trait仅仅是一个有约束的trait,* 不是 * 它的supertraits接口的扩展。
从概念上讲,通过声明trait Tr2: Tr1,您只是说如果类型T实现了Tr2,那么它也必须实现Tr1,但是trait Tr1Tr2是完全独立的,并且每个trait都为其关联项创建了一个单独的命名空间(方法、关联类型、常量等)。任何使用T的代码都可以导入和使用Tr1Tr2,两者都可以,或者都不可以;但如果它只使用其中一个,它甚至不必考虑另一个存在。
更具体地说,通过使每个trait成为一个单独的命名空间,Rust避免了众所周知的fragile base class problem。每个trait都可以定义自己的操作,如果没有专门导入到命名空间中,这些操作可以保证不会与任何其他操作发生冲突。因此,完全可以这样写(playground link),尽管可能有点不明智:

mod m {
    pub trait Foo {
        fn xyzzy(&self) { println!("foo!"); }
    }
    
    pub trait Bar {
        fn xyzzy(&self) { println!("bar!"); }
    }
    
    pub trait Quux: Foo+Bar {
        fn xyzzy(&self) { println!("quux!"); }
    }
    
    pub struct X;
    impl Foo for X {}
    impl Bar for X {}
    impl Quux for X {}
}

use m::X;

fn main() {
    let x = X;
    { 
        use m::Foo;
        x.xyzzy();  // prints "foo!"
    }
    {
        use m::Quux;
        x.xyzzy();  // prints "quux!"
    }
}

将上面的use m::Foo;替换为use m::Bar;,看看行为如何变化。

qgzx9mmu

qgzx9mmu2#

因为它们是不同的特征?
因为Person具有Display trait绑定,这意味着fmtPerson的成员。
不,不是。超trait是一个 * 需求,你也需要实现 *。它类似于你需要满足的trait上的where子句,只是有点不同。它不像OOP语言中的基类,使基类成为基类的一部分。
这是一个不同的特质,需要不同的实现方式。它可以有一个全面的实现,然后你根本不需要实现它。
当然,如果可以在subtrait中实现supertrait的方法,这也是可以解释的,但它更多的是语法糖,而不是实际的语义。我隐约记得这样的提议,但我可能是错的。

相关问题