supertrait 是「trait 的继承」:一个 trait 要求实现者先实现另一个。这篇用真实编译逐条验证它的规则:要求怎么体现、泛型约束里的传递、trait 对象能不能向上转型(这个能力在近几个版本才稳定),以及同名方法的歧义。
源码:
1 | use std::fmt::Display;
2 | trait Pretty: Display {
3 | fn pretty(&self) -> String { format!("★ {} ★", self) } // 能用 Display,因为它是 supertrait
4 | }
5 | struct Name(&'static str);
6 | impl Display for Name { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.0) } }
7 | impl Pretty for Name {}
8 | fn main() { println!("{}", Name("rust").pretty()); }
编译:成功
运行(退出码 0):
★ rust ★
源码:
1 | use std::fmt::Display;
2 | trait Pretty: Display { fn pretty(&self) -> String { format!("★ {} ★", self) } }
3 | struct Plain;
4 | impl Pretty for Plain {}
5 | fn main() {}
编译:失败
error[E0277]: `Plain` doesn't implement `std::fmt::Display`
--> <实验目录>/src/u45b.rs:4:17
|
4 | impl Pretty for Plain {}
| ^^^^^ unsatisfied trait bound
|
help: the trait `std::fmt::Display` is not implemented for `Plain`
--> <实验目录>/src/u45b.rs:3:1
|
3 | struct Plain;
| ^^^^^^^^^^^^
note: required by a bound in `Pretty`
--> <实验目录>/src/u45b.rs:2:15
|
2 | trait Pretty: Display { fn pretty(&self) -> String { format!("★ {} ★", self) } }
| ^^^^^^^ required by this bound in `Pretty`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`. 源码:
1 | trait Named { fn name(&self) -> String; }
2 | trait Aged { fn age(&self) -> u32; }
3 | trait Person: Named + Aged { fn intro(&self) -> String { format!("{},{} 岁", self.name(), self.age()) } }
4 | struct Ann; impl Named for Ann { fn name(&self) -> String { "Ann".into() } } impl Aged for Ann { fn age(&self) -> u32 { 30 } } impl Person for Ann {}
5 | fn greet<T: Person>(p: &T) -> String { format!("你好 {}(也能直接用 name():{})", p.intro(), p.name()) } // T: Person 就够了,不需要再写 Named + Aged
6 | fn main() { println!("{}", greet(&Ann)); }
编译:成功
运行(退出码 0):
你好 Ann,30 岁(也能直接用 name():Ann) 源码:
1 | trait Named { fn name(&self) -> String; }
2 | trait Person: Named { fn id(&self) -> u32; }
3 | struct Ann; impl Named for Ann { fn name(&self) -> String { "Ann".into() } } impl Person for Ann { fn id(&self) -> u32 { 7 } }
4 | fn only_named(n: &dyn Named) -> String { n.name() }
5 | fn main() {
6 | let p: &dyn Person = &Ann;
7 | println!("通过 &dyn Person 调用 id() = {},name() = {}", p.id(), p.name());
8 | println!("向上转型成 &dyn Named:{}", only_named(p));
9 | let b: Box<dyn Person> = Box::new(Ann); let n: Box<dyn Named> = b;
10 | println!("Box<dyn Person> 转成 Box<dyn Named>:{}", n.name());
11 | }
编译:成功
运行(退出码 0):
通过 &dyn Person 调用 id() = 7,name() = Ann
向上转型成 &dyn Named:Ann
Box<dyn Person> 转成 Box<dyn Named>:Anntrait 对象的基础见Trait Object 与动态分发。
源码:
1 | trait A { fn hello(&self) -> &'static str { "A" } }
2 | trait B { fn hello(&self) -> &'static str { "B" } }
3 | trait C: A + B { fn call(&self) -> &'static str { self.hello() } }
4 | fn main() {}
编译:失败
error[E0034]: multiple applicable items in scope
--> <实验目录>/src/u45e.rs:3:56
|
3 | trait C: A + B { fn call(&self) -> &'static str { self.hello() } }
| ^^^^^ multiple `hello` found
|
note: candidate #1 is defined in the trait `A`
--> <实验目录>/src/u45e.rs:1:11
|
1 | trait A { fn hello(&self) -> &'static str { "A" } }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in the trait `B`
--> <实验目录>/src/u45e.rs:2:11
|
2 | trait B { fn hello(&self) -> &'static str { "B" } }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the method for candidate #1
|
3 - trait C: A + B { fn call(&self) -> &'static str { self.hello() } }
3 + trait C: A + B { fn call(&self) -> &'static str { A::hello(&self) } }
|
help: disambiguate the method for candidate #2
|
3 - trait C: A + B { fn call(&self) -> &'static str { self.hello() } }
3 + trait C: A + B { fn call(&self) -> &'static str { B::hello(&self) } }
|
…(共 30 行)
源码:
1 | trait A { fn hello(&self) -> &'static str { "A" } }
2 | trait B { fn hello(&self) -> &'static str { "B" } }
3 | trait C: A + B { fn call(&self) -> String { format!("{} {}", A::hello(self), B::hello(self)) } }
4 | struct S; impl A for S {} impl B for S {} impl C for S {}
5 | fn main() { println!("{}", S.call()); }
编译:成功
运行(退出码 0):
A B类似的歧义在Trait 定义与实现里也出现过。