目标:为任何具有
to_string()
的类型实现特征的默认实现。
所以我的方法实际上可能是完全错误的。但我首先以这种方式问这个问题,因为它仍然是一个有趣的问题。
use core::fmt::Display
trait MyStyle<T: Display> {
fn pretty(&self) -> String {
let buf = self.to_string();
// manipulate buf
buf
}
}
上面的代码中,
self
到底是什么?我的环境(通过rust-analyzer
)只是告诉我self: &Self // size = 8, align = 0x8
。但它是什么类型?
显然这段代码不起作用,否则我就不会在这里,因为我明白了
方法
存在供参考to_string
,但不满足其特征界限。&Self
如果它说
to_string
存在,则表明 self
可能是对 Display
的一些引用,但我不明白其余部分,以及为什么它无法编译。
Self
指的是实现该特征的类型。因此,在特征定义中,它代表实现该特征的any类型。您可以将其视为通用的 S
,其中 S: MyTrait<T>
。它并不指一种单一类型。
目标:为任何具有 to_string() 的类型实现特征的默认实现。
这非常简单,通过使你的特质变得通用,你可能会遇到过于复杂的事情。这可能就是您想要的:
use std::fmt::Display;
trait MyStyle: Display {
fn pretty(&self) -> String {
let buf = self.to_string();
// manipulate buf
buf
}
}
可能需要额外的
impl<T> MyStyle for T where T: Display {}
来为任何实现 Display
的内容创建一个全面的实现。
在特征实现中,
&self
基本上是self: &Self
的语法糖,Self
是实现该特征的实际类型的占位符类型。这在定义类似构造函数的关联方法的特征中尤其明显,例如 Default
特征,本质上是:
trait Default {
fn default() -> Self;
}
struct A {};
impl Default for A {
fn default() -> A { // NOTE: Self == A here
A {}
}
}
请注意,在通用特征上添加特征边界是相当严格的。更惯用的做法是使特征完全通用,然后定义一个全面的实现。例如
trait MyStyle {
fn pretty(&self) -> String;
}
impl<T> MyStyle for T where T: Display {
fn pretty(&self) -> String {
let buf = format!("-~<[{}]>~-", self);
buf
}
}
fn main() {
println!("{}", 123.pretty());
}
打印:
-~<[123]>~-
方法
存在供参考to_string
,但不满足其特征界限 不满足以下特征界限:&Self
Self: std::fmt::Display
措辞有点混乱。编译器不是说方法
to_string()
存在于for&Self
。这就是说方法 to_string()
存在,句号。名称 to_string()
指的是实际方法。
例如,如果您改为调用
self.foo_bar_baz()
(该名称不存在)。
let buf = self.foo_bar_baz();
然后编译器会说:
在当前范围内未找到名为
的方法供参考foo_bar_baz
&Self
中找不到方法&Self
因为名为
foo_bar_baz
的方法不存在。所以这只是说to_string()
存在。并不是说它专门为&Self
而存在。
另外,编译器表示
to_string()
存在。然而,特征边界Self: std::fmt::Display
并未得到满足。所以不可能给 .to_string()
打电话 &Self
。