题目来自:https://practice-zh.course.rs/generics-traits/trait-object.html
1
trait Bird {
fn quack(&self) -> String;
}
struct Duck;
impl Duck {
fn swim(&self) {
println!("Look, the duck is swimming")
}
}
struct Swan;
impl Swan {
fn fly(&self) {
println!("Look, the duck.. oh sorry, the swan is flying")
}
}
impl Bird for Duck {
fn quack(&self) -> String{
"duck duck".to_string()
}
}
impl Bird for Swan {
fn quack(&self) -> String{
"swan swan".to_string()
}
}
fn main() {
// 填空
let duck = __;
duck.swim();
let bird = hatch_a_bird(2);
// 变成鸟儿后,它忘记了如何游,因此以下代码会报错
// bird.swim();
// 但它依然可以叫唤
assert_eq!(bird.quack(), "duck duck");
let bird = hatch_a_bird(1);
// 这只鸟儿忘了如何飞翔,因此以下代码会报错
// bird.fly();
// 但它也可以叫唤
assert_eq!(bird.quack(), "swan swan");
println!("Success!")
}
// 实现以下函数
fn hatch_a_bird...
一个答案示例:
fn main() {
// 填空
let duck = Duck;
duck.swim();
let bird = hatch_a_bird(2);
// 变成鸟儿后,它忘记了如何游,因此以下代码会报错
// bird.swim();
// 但它依然可以叫唤
assert_eq!(bird.quack(), "duck duck");
let bird = hatch_a_bird(1);
// 这只鸟儿忘了如何飞翔,因此以下代码会报错
// bird.fly();
// 但它也可以叫唤
assert_eq!(bird.quack(), "swan swan");
println!("Success!")
}
// 实现以下函数
fn hatch_a_bird(num: i32) -> Box<dyn Bird>{
match num {
2 => Box::new(Duck),
_ => Box::new(Swan)
}
}
Box
类似于C++的智能指针,当然,Rust没有提供传统指针(至少我还没学到),而dyn
有点虚类的味道了,都是运行时语法。不过这里不能使用引用,因为没有被引用的主体。
2
trait Bird {
fn quack(&self);
}
struct Duck;
impl Duck {
fn fly(&self) {
println!("Look, the duck is flying")
}
}
struct Swan;
impl Swan {
fn fly(&self) {
println!("Look, the duck.. oh sorry, the swan is flying")
}
}
impl Bird for Duck {
fn quack(&self) {
println!("{}", "duck duck");
}
}
impl Bird for Swan {
fn quack(&self) {
println!("{}", "swan swan");
}
}
fn main() {
// 填空
let birds __;
for bird in birds {
bird.quack();
// 当 duck 和 swan 变成 bird 后,它们都忘了如何翱翔于天际,只记得该怎么叫唤了。。
// 因此,以下代码会报错
// bird.fly();
}
}
这里的比喻很生动形象了,在没有声明vec的元素类型的情况下,vec只会按第一个推导;声明了vec是特征,那子类的各种自己的方法就用不了了。C++也是一样。
fn main() {
// 填空
let birds: Vec<Box<dyn Bird>> = vec![Box::new(Duck), Box::new(Swan)];
for bird in birds {
bird.quack();
// 当 duck 和 swan 变成 bird 后,它们都忘了如何翱翔于天际,只记得该怎么叫唤了。。
// 因此,以下代码会报错
// bird.fly();
}
}
3
// 填空
trait Draw {
fn draw(&self) -> String;
}
impl Draw for u8 {
fn draw(&self) -> String {
format!("u8: {}", *self)
}
}
impl Draw for f64 {
fn draw(&self) -> String {
format!("f64: {}", *self)
}
}
fn main() {
let x = 1.1f64;
let y = 8u8;
// draw x
draw_with_box(__);
// draw y
draw_with_ref(&y);
println!("Success!")
}
fn draw_with_box(x: Box<dyn Draw>) {
x.draw();
}
fn draw_with_ref(x: __) {
x.draw();
}
答案:
fn main() {
let x = 1.1f64;
let y = 8u8;
// draw x
draw_with_box(Box::new(x));
// draw y
draw_with_ref(&y);
println!("Success!")
}
fn draw_with_box(x: Box<dyn Draw>) {
x.draw();
}
fn draw_with_ref(x: &dyn Draw) {
x.draw();
}
4
trait Foo {
fn method(&self) -> String;
}
impl Foo for u8 {
fn method(&self) -> String { format!("u8: {}", *self) }
}
impl Foo for String {
fn method(&self) -> String { format!("string: {}", *self) }
}
// 通过泛型实现以下函数
fn static_dispatch...
// 通过特征对象实现以下函数
fn dynamic_dispatch...
fn main() {
let x = 5u8;
let y = "Hello".to_string();
static_dispatch(x);
dynamic_dispatch(&y);
println!("Success!")
}
答案
// 通过泛型实现以下函数
fn static_dispatch<T: Foo>(t: T) -> String{
t.method()
}
// 通过特征对象实现以下函数
fn dynamic_dispatch(f: &dyn Foo) -> String{
f.method()
}
对于C++来说,静态多态一般指的是重载,一旦涉及父类子类调用,就是动态多态的部分了,指针和引用都是一样的。
5
// 使用至少两种方法让代码工作
// 不要添加/删除任何代码行
trait MyTrait {
fn f(&self) -> Self;
}
impl MyTrait for u32 {
fn f(&self) -> Self { 42 }
}
impl MyTrait for String {
fn f(&self) -> Self { self.clone() }
}
fn my_function(x: Box<dyn MyTrait>) {
x.f()
}
fn main() {
my_function(Box::new(13_u32));
my_function(Box::new(String::from("abc")));
println!("Success!")
}
特征安全需要方法不能返回Self,所以第一种改法就是将所有的返回值改成Box
包裹动态类型:
trait MyTrait {
fn f(&self) -> Box<dyn MyTrait>;
}
impl MyTrait for u32 {
fn f(&self) -> Box<dyn MyTrait> { Box::new(42) }
}
impl MyTrait for String {
fn f(&self) -> Box<dyn MyTrait> { Box::new(self.clone()) }
}
fn my_function(x: Box<dyn MyTrait>) -> Box<dyn MyTrait>{
x.f()
}
fn main() {
my_function(Box::new(13_u32));
my_function(Box::new(String::from("abc")));
println!("Success!")
}
第二种和第一种类似,只不过是把所有Box改成了动态引用
trait MyTrait {
fn f(&self) -> &dyn MyTrait;
}
impl MyTrait for u32 {
fn f(&self) -> &dyn MyTrait { &42 }
}
impl MyTrait for String {
fn f(&self) -> &dyn MyTrait { self }
}
fn my_function(x: &dyn MyTrait) -> &dyn MyTrait{
x.f()
}
fn main() {
my_function(&13_u32);
my_function(&String::from("abc"));
println!("Success!")
}
你要是问我性能差别,我只能说两种都是栈上指针,堆上数据,性能应该没啥差别
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 【Rust练习】18.特征 Trait
发表评论 取消回复