阅读 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 的工作原理”,但我想了解原因。
其 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);
}
}
因为他们的特质不同?
Person
Display
特征绑定,这意味着
是fmt
的成员。Person
不,不是。超级特质是一个要求,也是你需要实现的。它类似于关于您需要满足的特征的
where
子句,只是有点不同。它不像例如OOP 语言中的基类,使基类成为基类的一部分。 这是一个不同的特征,需要以不同的方式实施。它可以有一个全面的实现,然后你根本不需要实现它。
当然,如果可以在子特征中实现超级特征的方法也是可以解释的,但它更多的是语法糖而不是实际的语义。我依稀记得有这样的建议,但我可能是错的。