這兩個內容都比較容易理解。
一、新類型(newtype)模式
注意,這裏説的是一種編程模式,不是説有一個叫newtype的類型。
這種編程模式的含義:為某個類型作個封裝,構建一個新的類型,以便繞過某些束縛,從而達成特定目的。
目的如下:
a.用於抽象掉一些類型的實現細節
b.可以隱藏其內部的泛型類型
c.實現曲線救國
一個典型的例子,利用新類型繞過孤兒規則。
二、類型別名
語法
type xxx=現有的類型
看起來有點像特質中的關聯類型定義。
附:特質關聯類型實現示例
impl Fight for Person {
type Item = Animal;
fn attack(&self, other: &self::Item) {
println!(
"{}歲{}(性別:{}) 攻擊了 {}歲{}",
self.age,
self.name,
self.sex,
other.age,
other.name
);
}
fn defend<T: Danger>(&self, danger: &T) {
println!("{},{}歲{}(性別:{}) 奮起併力圖戰勝它們",danger.happen(), self.age, self.name, self.sex);
}
}
要點
a.簡化類型書寫
b.使得代碼更容易閲讀
c.也更方便維護,例如如果許多方法都要求用同個類型,那麼這樣定義之後就不容易搞錯,
作用上和java中定義的枚舉常量一個道理。
在rust的標準庫中,有不少的類型別名,例如Result<T,E>等。
定義説明
a.可以為別名定義別名
b.沒有明確的別名層級限定,意思就是你定義了別名a,然後基於a定義b,依次類推,可以定義type n=m.. .有多少層級限定不知道,不過定義個2次應該沒有問題的。
rust的類型別名是一種類似Microsoft SqlServer自定義類型的東西。
在 SQL Server 中,可以通過 用户定義數據類型(User-Defined Data Types, UDT) 創建自定義類型,本質上是基於系統數據類型的別名,並附加約束或默認值。
例如:
-- 創建類型:限制長度並校驗格式
CREATE TYPE dbo.Email
FROM VARCHAR(255)
NOT NULL
WITH CHECK (CHARINDEX('@', value) > 0);
-- 使用類型
CREATE TABLE Users (
UserID INT PRIMARY KEY,
UserEmail dbo.Email
);
-- 測試插入
INSERT INTO Users VALUES (1, 'test@example.com'); -- 成功
INSERT INTO Users VALUES (2, 'invalid-email'); -- 失敗,觸發 CHECK 約束
只要做過數據庫設計,就知道這個東西多好用! 好記、容易保證相同的屬性使用相同的類型、維護也非常容易。
如果修改了type,就會發現有的時候,這真是一個絕妙的注意。
這種好東西在postgresql和mysql(8以上)都有類似的實現。
所以,毫無疑問,在rust中這是一個絕妙主意,因為我們都知道rust的類型之複雜醜陋是出名的,有的時候讓人心煩,又會耗費很多時間打字,而又了別名就會在某些情況下,大大改善這個現象。
三、新類型示例
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是外部包類型。
四、類型別名示例
struct Person {
name: String,
age: u8,
}
type Teacher = Person;
type Student = Person;
type GoodPeople = Student;
type Thunk = Box<dyn Fn() + Send + 'static>;
fn print_student(p: &Student) {
println!("name={},age={}", p.name, p.age);
}
fn print_me(p: &GoodPeople) {
println!("name={},age={}", p.name, p.age);
}
fn main() {
let f: Thunk = Box::new(|| println!("hi"));
//應為Thunk是Box類型,已經實現了Deref特質,它可以自動解引用,所以可以直接調用。
f();
//如果想顯式調用,可以這樣寫:
(*f)();
let p = Student {
name: "張三".to_string(),
age: 30,
};
print_student(&p);
let g = GoodPeople {
name: "lzf".to_string(),
age: 99,
};
print_me(&g);
}
#[allow(unused)]
fn takes_long_type(f: Thunk) {
// --snip--
}
#[allow(unused)]
fn returns_long_type() -> Thunk {
// --snip--
Box::new(|| {})
}
上例中,Student是Person別名,GoodPerson是Student的別名。
運行結果:

五、小結
1.類型別名的確是一個很好的東西
2.只要寫過代碼,就知道它能讓代碼更簡單、易讀、易維護