回忆下孤儿规则:
1.只有当一个trait或类型在当前的crate中定义时,才能为外部类型实现该trait。 没有限定是特质还是类型
反过来,如果特质和类型都是外部,那么不能在当前单元包实现
2.例外情况-std中特质是例外。大体验证了凡事都有例外
孤儿规则的目的:避免编译器无法确定应该用哪一个实现。
这个目的很容易理解。无论是否叫孤儿,绝大部分语言中都是类似的规则-不允许一个类型实现接口/特质的时候,有多处不一样代码。
书本上主要讨论如何绕过孤儿规则,方案就是:对外部类型进行封装,构建一个新的本地类型,然后就可以实现本地类型的外部特质,从而间接实现外部类型的外部特质。
不过这本质上是一种绕过问题的解决方案,在有些场合中也可以将就使用。在rust中有提到相对直接的解决方法,不过本文暂不论述。
一、孤儿规则示例
如下图,这是一个包含了三个单元的工作空间:
其中wsmain是二进制单元,对于wsmain而言,student和teacher两个都是外部crate,看wsmain的Cargo.toml就明白了:- [package]
- name = "wsmain"
- version.workspace=true
- edition.workspace=true
- [dependencies]
- student={path="../student"}
- teacher={path="../teacher"}
复制代码 本例中分别在teacher包中定义了特质Print,并在wsmain中尝试为Teacherinfo(teacher中的struct)实现Print,为了节省篇幅,放在一起:- //在teacher包
- pub trait Print{
- fn print(&self);
- }
- pub struct Teacherinfo{
- pub name: String,
- pub age: u8,
- pub gender: String,
- pub position: String
- }
- //在wsmain二进制单元包中
- impl Print for Teacherinfo{
- fn print(&self){
- println!("{}",self.name);
- }
- }
复制代码 如果你尝试运行,那么回得到如下提示:
二、用包装类绕过孤儿规则
何谓包装类,此处不介绍,直接看示例代码:- use student::*;
- use teacher::*;
- fn main() {
- let lml = Studentinfo {
- name: String::from("lml"),
- age: 18,
- gender: String::from("女"),
- no: String::from("12101"),
- };
- print_student(&lml);
- lml.learn();
- lml.sleep();
- let lu = Teacherinfo {
- name: String::from("lu"),
- age: 46,
- gender: String::from("男"),
- position: String::from("教研组长"),
- };
- lu.teach_student(&lml);
- print_teacher(&lu);
- let _me = Box::new(String::from("中国"));
- let my_lu = MyTeacherinfo(lu);
- my_lu.print();
- }
- struct MyTeacherinfo(Teacherinfo);
- impl Print for MyTeacherinfo {
- fn print(&self) {
- println!(
- "教师基本信息-姓名:{},年龄:{},性别:{},职位:{}",
- self.0.name, self.0.age, self.0.gender, self.0.position
- );
- }
- }
复制代码 在这个示例中,定义了一个新的结构,成员只包含一个Teacherinfo。用的是使用元组定义结构的方式。
看运行结果:
三、小结
这种利用包装模式绕过的方法有时候也能起到作用,只不过,我更愿意听到直面问题的方案。
利用包装模式是rust直常常提到的newtype模式的一种。所谓newtype,大概可以这样理解:基于特定构建一个新的类型,这样可以绕过一些问题,达成一些目的。
用包装器来绕过,还是有些小麻烦,倒也不是不能用!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |