
19 高级类型
使用newtype模式 实现类型安全与抽象
比如是u32类型封装一下成Millimeters 和 Meters,这样相同的值就有不同的单位,不会混淆。
新的类型可以暴露与其私有内部类型不同的共有 API。
类型同义词与类型别名
使用type 关键字为现有类型赋予另一个名字。类型别名的主要用途是减少重复。
在函数签名和类型注解中到处书写这个冗长的类型既乏味又容易出错。
没有返回 never type
Rust 有一个叫做! 的特殊类型。在类型理论术语中被称为 empty type,因为它没有值。我们更倾向于称之为 never type。
.
这个类型用于根本无法得出值的地方,所以在转换具体值之前,代码就会跳转。
可以用! never type 来填充到只有某类型的表达值的地方,特别是match 语句的分支。
loop{
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue, // 这里 continue返回的就是`!` never type类型。
};
}
...
impl<T> Option<T> {
pub fn unwrap(self) -> T {
match self {
Some(val) => val,
None => panic!("called `Option::unwrap()` on a `None` value"),
// panic! 返回的也是`!`类型,不然无法通过编译器。
}
}
}
- 循环中的 continue; break;
- 函数直接 reuturn;
- panic!()
- 没有出口的
loop循环
循环永远也不结束,所以此表达式的值是 !。但是如果引入 break 这就不为真了
动态大小类型和Sized trait
因为要求栈帧的大小必须是确定的,所以动态大小类型无法将数据存储在栈上,但可以通过引用、指针等方式来操作动态大小类型的数据。
因为字符串类型即可存放短字符串,又可存放长字符串,任意长度的字符串都可以。所以我们只能将字符串设置为动态大小类型。
str,也就是字符串类型,我们在栈上基本只能使用&str
为了与trait区分,强制使用 dyn trait来表示 trait object类型,这个类型是动态类型,我们只能使用&dyn trait或者Box<dyn trait>
Sized trait与?Sized?
[!summary] Rust 提供了 Sized trait 来决定一个类型的大小是否在编译时可知
只有实现了Sized trait 的 类型才能在运行时被操作系统调用size_of::<T>()了获取到类型的内存大小。而动态类型则没有实现这个 trait。
[!warning] 引用不是类型,指针也不是类型,只有&某类型是类型,Box<某类型>是类型
也就是&不能代表一个类型,Box<?>也不能代表一个类型,他们只是一个类型的组装部分。
默认情况下,类型参数从参数中提取,如果参数是引用或者指针,那么实际类型可能是固定大小也可能是动态大小类型。编译器默认认为是固定大小,会尝试获取类型大小分配在栈上。
(让编译器不要追查真正的大小,直接使用引用和指针的大小即可。)
标注参数类型可能不是Sized之后,编译器只会尝试引用和指针本身分配在栈上,而背后的数据会直接安排分配在堆上。
