阅读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`
我的问题是为什么?
因为我定义了Display
是Person
的supertrait,所以Person
trait的实现需要Display
trait也被实现。trait只是一个collection of methods,所以我希望Person
trait的所有方法都可以在同一个块中实现。
语句 “method fmt
is not a member of trait Person
“ 看起来很奇怪,因为Person
绑定了Display
trait,这意味着fmt
是Person
的成员。
这可以通过简单地完全 * 省略 * impl Display for Student
块来进一步证明,这会导致编译器告诉我 “trait std::fmt::Display
is not implemented for Student
”。因此不可能实现Person
,但不实现Display
。
我知道答案可能只是 “这就是Rust的工作方式”,但我想知道原因。
2条答案
按热度按时间u3r8eeie1#
一个声明了supertraits的trait仅仅是一个有约束的trait,* 不是 * 它的supertraits接口的扩展。
从概念上讲,通过声明
trait Tr2: Tr1
,您只是说如果类型T
实现了Tr2
,那么它也必须实现Tr1
,但是traitTr1
和Tr2
是完全独立的,并且每个trait都为其关联项创建了一个单独的命名空间(方法、关联类型、常量等)。任何使用T
的代码都可以导入和使用Tr1
或Tr2
,两者都可以,或者都不可以;但如果它只使用其中一个,它甚至不必考虑另一个存在。更具体地说,通过使每个trait成为一个单独的命名空间,Rust避免了众所周知的fragile base class problem。每个trait都可以定义自己的操作,如果没有专门导入到命名空间中,这些操作可以保证不会与任何其他操作发生冲突。因此,完全可以这样写(playground link),尽管可能有点不明智:
将上面的
use m::Foo;
替换为use m::Bar;
,看看行为如何变化。qgzx9mmu2#
因为它们是不同的特征?
因为
Person
具有Display
trait绑定,这意味着fmt
是Person
的成员。不,不是。超trait是一个 * 需求,你也需要实现 *。它类似于你需要满足的trait上的
where
子句,只是有点不同。它不像OOP语言中的基类,使基类成为基类的一部分。这是一个不同的特质,需要不同的实现方式。它可以有一个全面的实现,然后你根本不需要实现它。
当然,如果可以在subtrait中实现supertrait的方法,这也是可以解释的,但它更多的是语法糖,而不是实际的语义。我隐约记得这样的提议,但我可能是错的。