blanket impl 是 Rust 里「为一类类型一次性实现 trait」的办法,标准库大量使用它:ToString、Into 都是。这篇验证它的工作方式、两个最典型的标准库例子、扩展 trait 模式,以及一个常被忽略的细节 —— 引用类型要不要单独实现。
源码:
1 | use std::fmt::Display;
2 | trait Loud { fn loud(&self) -> String; }
3 | impl<T: Display> Loud for T { fn loud(&self) -> String { format!("{}!!!", self).to_uppercase() } }
4 | struct Name(&'static str);
5 | impl Display for Name { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.0) } }
6 | fn main() {
7 | println!("{} {} {} {}", "hello".loud(), 42.loud(), 3.5.loud(), Name("rust").loud()); // 任何实现了 Display 的类型都有 loud()
8 | }
编译:成功
运行(退出码 0):
HELLO!!! 42!!! 3.5!!! RUST!!!
编译:失败
error[E0599]: `NoDisplay` doesn't implement `std::fmt::Display`
--> <实验目录>/src/u48b.rs:5:38
|
4 | struct NoDisplay;
| ---------------- method `loud` not found for this struct because it doesn't satisfy `NoDisplay: Loud` or `NoDisplay: std::fmt::Display`
5 | fn main() { println!("{}", NoDisplay.loud()); }
| ^^^^ method cannot be called on `NoDisplay` due to unsatisfied trait bounds
|
note: trait bound `NoDisplay: std::fmt::Display` was not satisfied
--> <实验目录>/src/u48b.rs:3:9
|
3 | impl<T: Display> Loud for T { fn loud(&self) -> String { format!("{}!!!", self) } }
| ^^^^^^^ ---- -
| |
| unsatisfied trait bound introduced here
note: the trait `std::fmt::Display` must be implemented
--> <rust 标准库源码>/library/core/src/fmt/mod.rs:1187:1
|
1187 | pub trait Display: PointeeSized {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: items from traits can only be used if the trait is implemented and in scope
note: `Loud` defines an item `loud`, perhaps you need to implement it
--> <实验目录>/src/u48b.rs:2:1
|
2 | trait Loud { fn loud(&self) -> String; }
| ^^^^^^^^^^
…(共 30 行)与孤儿规则的冲突限制见Trait 实现冲突与孤儿规则。
源码:
1 | use std::fmt;
2 | struct Celsius(f64);
3 | impl fmt::Display for Celsius { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:.1}°C", self.0) } }
4 | struct Meters(f64);
5 | struct Km(f64);
6 | impl From<Km> for Meters { fn from(k: Km) -> Meters { Meters(k.0 * 1000.0) } }
7 | fn main() {
8 | println!("只实现了 Display,免费得到 to_string():{}", Celsius(36.6).to_string());
9 | let m: Meters = Km(2.5).into(); // 只实现了 From,免费得到 Into
10 | println!("只实现了 From<Km> for Meters,免费得到 Into:{} 米", m.0);
11 | }
编译:成功
运行(退出码 0):
只实现了 Display,免费得到 to_string():36.6°C
只实现了 From<Km> for Meters,免费得到 Into:2500 米
源码:
1 | struct Meters(f64); struct Km(f64);
2 | impl Into<Meters> for Km { fn into(self) -> Meters { Meters(self.0 * 1000.0) } } // 只实现 Into
3 | fn main() { let _m: Meters = Meters::from(Km(1.0)); }
编译:失败
error[E0308]: mismatched types
--> <实验目录>/src/u48d.rs:3:43
|
3 | fn main() { let _m: Meters = Meters::from(Km(1.0)); }
| ------------ ^^^^^^^ expected `Meters`, found `Km`
| |
| arguments to this function are incorrect
|
note: associated function defined here
--> <rust 标准库源码>/library/core/src/convert/mod.rs:594:8
|
594 | fn from(value: T) -> Self;
| ^^^^
help: call `Into::into` on this expression to convert `Km` into `Meters`
|
3 | fn main() { let _m: Meters = Meters::from(Km(1.0).into()); }
| +++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.From / Into 的详细用法见From、Into 与类型转换。
源码:
1 | trait StrExt { fn shout(&self) -> String; fn word_count(&self) -> usize; }
2 | impl<T: AsRef<str> + ?Sized> StrExt for T {
3 | fn shout(&self) -> String { self.as_ref().to_uppercase() + "!" }
4 | fn word_count(&self) -> usize { self.as_ref().split_whitespace().count() }
5 | }
6 | fn main() {
7 | let s = String::from("hello rust world");
8 | let c: std::borrow::Cow<str> = std::borrow::Cow::Borrowed("cow str");
9 | println!("&str:{} {}", "a b".shout(), "a b".word_count());
10 | println!("String:{} {}", s.shout(), s.word_count());
11 | println!("Cow:{} {}", c.shout(), c.word_count());
12 | }
编译:成功
运行(退出码 0):
&str:A B! 2
String:HELLO RUST WORLD! 3
Cow:COW STR! 2 编译:失败
error[E0277]: the trait bound `&Dog: Speak` is not satisfied
--> <实验目录>/src/u48f.rs:4:60
|
4 | fn main() { println!("{}", talk(Dog)); println!("{}", talk(&Dog)); }
| ---- ^^^^ the trait `Speak` is not implemented for `&Dog`
| |
| required by a bound introduced by this call
|
note: required by a bound in `talk`
--> <实验目录>/src/u48f.rs:3:12
|
3 | fn talk<S: Speak>(s: S) -> String { s.speak() }
| ^^^^^ required by this bound in `talk`
help: consider removing the leading `&`-reference
|
4 - fn main() { println!("{}", talk(Dog)); println!("{}", talk(&Dog)); }
4 + fn main() { println!("{}", talk(Dog)); println!("{}", talk(Dog)); }
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
源码:
1 | trait Speak { fn speak(&self) -> String; }
2 | struct Dog; impl Speak for Dog { fn speak(&self) -> String { "汪".into() } }
3 | impl<T: Speak + ?Sized> Speak for &T { fn speak(&self) -> String { (**self).speak() } }
4 | impl<T: Speak + ?Sized> Speak for Box<T> { fn speak(&self) -> String { (**self).speak() } }
5 | fn talk<S: Speak>(s: S) -> String { s.speak() }
6 | fn main() { println!("{} {} {}", talk(Dog), talk(&Dog), talk(Box::new(Dog))); }
编译:成功
运行(退出码 0):
汪 汪 汪