10玩rust_C++工程师的Rust迁移之路(5)- 继承与组合 - 下
2020-11-25 更新:
- 修正了C++ 20中的concept语法
在上一篇文章 https://zhuanlan.zhihu.com/p/76740667 中,我介绍多态、静态分发和动态分发的概念,以及他们各自在C++和Rust中的实现方式。
在本文中,我会重点讲Rust中的Trait实现的静态分发与C++ 20(准确的说,现在还叫做C++ 2a)中的concepts的区别。
在具体介绍这个区别之前,我想跟大家介绍一个概念,叫做duck typing(鸭子类型)。
鸭子类型
呃……你没有看错,这个鸭子就是你平常理解的那个鸭子,我也没有翻译错……
鸭子类型[1]是鸭子测试的一个应用:
如果它走起来像鸭子,也跟鸭子一样发出嘎嘎的叫声,那么它就是鸭子
听起来似乎非常无厘头,但这个模式实际上被广泛的应用于多种语言。
在C++中的应用
template <typename T>
concept Stream = requires(T a, std::uint8_t* mut_buffer, size_t size, const std::uint8_t* buffer)
{{ a.read(mut_buffer, size) } -> std::convertible_to<size_t>;{ a.write(buffer, size) } -> std::convertible_to<size_t>;
};class Console { ... };
class FileStream { ... };
在Golang中的应用
type Stream interface {Read(uint32) []byteWrite([]byte) uint32
}type Console struct { ... }
type FileStream struct { ... }func (c Console) Read(size uint32) []byte {...
}func (c Console) Write(data []byte) uint32 {...
}
在上面的两个例子中,我们可以注意到,Console和FileStream这两个类型都没有显示的声明自己兼容Stream concept(interface),但在编译阶段,编译器可以根据他们实现的方法来判断他们支持Stream要求的操作,从而实现多态。
这个功能看似非常诱人,省去了显式声明的麻烦,但也带来了问题。
鸭子类型的局限性
程序员的造词能力通常是非常匮乏的(大家每次要给变量命名时的抓耳挠腮可以证明这一点),所以非常容易在方法名上重复,但在两个语境中又可能具有完全不同的语义。
举个例子:
template <typename T>
concept Thread = requires(T a, int signal) {{ a.kill(signal) };
};class DuckFlock {public:void kill(int amount);
};void nofity_thread(Thread& t) {t.kill(SIGUSR1);
}
原本我以为给鸭群发了一个信号,让它们打印一下状态,结果一不小心就杀掉了10只鸭子[2],真的只能召唤华农兄弟了。
Rust的设计
在Rust中,是不允许这种情况出现的,必须显式的生命类型实现的是哪个trait:
trait Thread {fn kill(&mut self, signal:i32);
}trait Flock {fn kill(&mut self, amount:i32);
}struct DuckFlock {ducks: i32
}impl DuckFlock {pub fn new(amount: i32) -> DuckFlock {DuckFlock{ ducks: amount }}
}impl Thread for DuckFlock {fn kill(&mut self, signal: i32) {if signal == 10 {println!("We have {} ducks", self.ducks);} else {println!("Unknown signal {}", signal);}}
}impl Flock for DuckFlock {fn kill(&mut self, amount: i32) {self.ducks -= amount;println!("{} ducks killed", amount);}
}fn main() {let mut flock = DuckFlock::new(100);{let thread:&mut Thread = &mut flock;thread.kill(10);}{let flock:&mut Flock = &mut flock;flock.kill(10);}{let thread:&mut Thread = &mut flock;thread.kill(10);}
}
同样的,这个例子我也放到Rust Playground,欢迎大家前去玩耍。
Markers
在Rust中,由于实现Trait必须要显式声明,这就衍生出了一种特殊类型的trait,它不包含任何的函数要求:
trait TonyFavorite {}
trait Food {fn name(&self) -> String;
}struct PeikingDuck;impl Food for PeikingDuck {fn name(&self) -> String {"Peiking Duck".to_owned()}
}impl TonyFavorite for PeikingDuck {}struct Liver;impl Food for Liver {fn name(&self) -> String {"Liver".to_owned()}
}fn eat<T: Food + TonyFavorite>(food: T) {println!("Tony only eat his favorite food like {}", food.name());
}fn main() {eat(PeikingDuck);// eat(Liver); // compile error
}
这里例子的Playground在此。
事实上,在Rust中,类似的Marker还有非常多,比如Copy、Sync、Send等等。在后续的文章中,再跟大家逐一解释这些trait的含义与妙用。
在下一节的文章中,我会介绍Rust类型系统和C++类型系统最大的不同之一:Rust结构体不能继承,以及为什么。敬请期待。
延伸阅读
上一篇
黄珏珅:C++工程师的Rust迁移之路(4)- 继承与组合 - 中zhuanlan.zhihu.com
下一篇
黄珏珅:C++工程师的Rust迁移之路(6)- 继承与组合 - 后zhuanlan.zhihu.com
参考
- ^Duck typing https://en.wikipedia.org/wiki/Duck_typing
- ^在Linux下SIGUSR1等于10
10玩rust_C++工程师的Rust迁移之路(5)- 继承与组合 - 下相关推荐
- 10玩rust_有趣的 Rust 类型系统: Trait
也许你已经学习了标准库提供的 String 类型,这是一个 UTF-8 编码的可增长字符串.该类型的结构为: pub struct String {vec: Vec<u8>, } UTF- ...
- Cris 玩转 Linux 之 Deepin 迁移全过程记录
Cris 玩转 Linux 之 Deepin 迁移全过程记录 Author:Cris 文章目录 Cris 玩转 Linux 之 Deepin 迁移全过程记录 Author:Cris 0. 序 1. 磨 ...
- 服务迁移之路 | Spring Cloud向Service Mesh转变 | 技术干货
戳蓝字"CSDN云计算"关注我们哦! 技术头条:干货.简洁.多维全面.更多云计算精华知识尽在眼前,get要点.solve难题,统统不在话下! 作者: 李宁 转自:博云技术社区 Sp ...
- 运维工程师打怪升级进阶之路 V2.0
很多读者伙伴们反应总结的很系统.很全面,无论是0基础初学者,还是有基础的入门者,或者是有经验的职场运维工程师们,都反馈此系列文章非常不错! 命名:<运维工程师打怪升级之路> 版本:V1.0 ...
- 考拉海购全面云原生迁移之路
今年 8 月底,入驻"阿里动物园"一周年的考拉海购首次宣布战略升级,在现有的跨境业务基础上,将重点从以"货"为中心变成以"人"为中心,全面发 ...
- [转] Web前端研发工程师编程能力飞升之路
[转] Web前端研发工程师编程能力飞升之路 分类: Javascript | 转载请注明: 出自 海玉的博客 今天看到这篇文章.写的非常有意思.发现自己还有很长的一段路要走. [背景] 如果你是刚进 ...
- 初中计算机课师徒结对活动记录,师徒结对活动记录表10张(师傅尚积东徒弟丁明路)(备课6节听课4节).doc...
师徒结对活动记录表10张(师傅尚积东徒弟丁明路)(备课6节听课4节).doc 2014年度师徒结对活动记录表(一)活动方式交流探讨活动地点教导处活动时间2014.4.11活动内容帮助徒弟:提高认识青年 ...
- 关于硬件工程师的真相:敢问路在何方?
关于硬件工程师的真相:敢问路在何方? 硬件工程师,曾经有多少人希望从事的职业?在别人眼里好像能够从事硬件设计需要你了解很多东西,可以从事这个职业之后才逐渐发现,硬件工程师处在一种非常难受的困境当中!想 ...
- 习题 11.10 将本章11.8节中的程序片段加以补充完善,成为一个完整的程序。在程序中使用继承和组合。在定义Professor类对象prof1时给出所有数据的初值,然后修改prof1的生日数据。。。
C++程序设计(第三版) 谭浩强 习题11.10 个人设计 习题 11.10 将本章11.8节中的程序片段加以补充完善,成为一个完整的程序.在程序中使用继承和组合.在定义Professor类对象pro ...
最新文章
- 使用Powershell将PST导入Exchange 2007
- 多视图几何三维重建实战系列- Cascade-MVSNet
- netty中的future和promise源码分析(二)
- 深入Java中文编码乱码问题及最优解决方法
- 站长就是个太监^_^
- centos6 nodejs 安装测试
- pythonweb啥意思_python-web-guide
- 深度技术win11旗舰稳定版v2021.07
- epel源mysql版本_centos网络yum源和epel源(2017可用首选)
- php案例之后台数据显示-- PDO版(php data object)
- 原版Win7注入USB3.0驱动和NVME驱动教程
- 答答星球微信答题小程序头脑王者源码带后台手机app开发排位pk
- 【Qt象棋游戏】05_象棋走棋规则——象、马、将、兵
- 记一次应急-插U盘之后文件夹全变成exe中毒(100%解决)
- 如何将ipad作为电脑的第二显示屏
- 企业邮箱托管外包后安全吗?企业邮箱安全须知
- IE6下图片的浏览剪裁与上传
- Java 鸡翁一值钱五Java_Java案例5:斐波那契数列,百钱百鸡
- MybatisPlus报错can not find lambda cache for this entity
- 大学物理·第7章恒定磁场
热门文章
- 如何在ASP.NET Core中自定义Azure Storage File Provider
- EF Core 数据库 Provider 一览
- 【活动(广州)】MonkeyFest2018 微软最有价值专家讲座
- 课程 预编译框架,开发高性能应用 - 微软技术暨生态大会 2018
- ASP.NET Core 2.0使用Autofac实现IOC依赖注入竟然能如此的优雅简便
- .NET的一点历史故事:招兵买马和聚义山林
- 云计算设计模式(四)——消费者的竞争模式
- .NET Core 使用 grpc 实现微服务
- C语言之strstr函数类似Java字符串的contain函数
- Android之用UncaughtExceptionHandler实现保存崩溃日志到sdcard目录下的文件夹