回憶下孤兒規則:
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,大概可以這樣理解:基於特定構建一個新的類型,這樣可以繞過一些問題,達成一些目的。
用包裝器來繞過,還是有些小麻煩,倒也不是不能用!