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

问题描述 投票:0回答:2

阅读 Rust 书中高级特征章节中的关于超级特征的部分。尝试将其应用到一个超级简单的具体示例中,但有些东西我不明白。

当我为某些自定义特征定义特征绑定/超级特征时,我被迫在单独的

impl
块中为特定结构编写该超级特征的实现。

这个例子有效:

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`

我的问题是为什么?

由于我将

Display
定义为
Person
的超级特征,因此
Person
特征 的实现也需要
Display
特征也被实现。特征只是方法的集合,所以我希望
Person
特征的所有方法都可以在同一个块中实现。

语句 “方法

fmt
不是特征
Person
的成员”
看起来很奇怪,因为
Person
具有
Display
特征界限,这意味着
fmt
Person
 的成员。

这可以通过简单地完全省略

impl Display for Student块来进一步证明,这会导致编译器告诉我

“特征
std::fmt::Display未针对
Student
实现”
。所以实现
Person
是不可能的,但是
实现Display
我知道答案可能只是

“这就是 Rust 的工作原理”

,但我想了解原因。

rust traits
2个回答
4
投票
不是

其 supertraits 接口的扩展。 从概念上讲,通过声明

trait Tr2: Tr1

,您只是说如果类型

T
实现了
Tr2
,它也必须实现
Tr1
,但是特征
Tr1
Tr2
是完全独立的,并且每个都创建为其关联项(方法、关联类型、常数等)。 任何使用
T
的代码都可以导入并使用
Tr1
Tr2
,两者都可以,或者都不使用;但如果它只使用其中之一,它甚至不必考虑其他现有的。
更具体地说,通过将每个特征设置为单独的命名空间,Rust 避免了众所周知的“脆弱基类问题”。  每个特征都可以定义自己的操作,如果没有专门导入到命名空间中,则保证不会与任何其他特征发生冲突。  因此,尽管可能有些不明智,但完全有可能写这样的东西(

playground link

): mod m { pub trait Foo { fn xyzzy(&self) { println!("foo!"); } } pub trait Quux: Foo { fn xyzzy(&self) { println!("quux!"); } } pub struct X; impl Foo 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::Quux; // x.xyzzy(); // ambiguous Foo::xyzzy(&x); Quux::xyzzy(&x); } }

因为他们的特质不同?

2
投票
因为

Person

具有
Display

特征绑定,这意味着

fmt
Person
的成员。

不,不是。超级特质是一个
要求,也是你需要实现的

。它类似于关于您需要满足的特征的
where

子句,只是有点不同。它不像例如OOP 语言中的基类,使基类成为基类的一部分。 这是一个不同的特征,需要以不同的方式实施。它可以有一个全面的实现,然后你根本不需要实现它。

当然,如果可以在子特征中实现超级特征的方法也是可以解释的,但它更多的是语法糖而不是实际的语义。我依稀记得有这样的建议,但我可能是错的。

© www.soinside.com 2019 - 2024. All rights reserved.