Skip to main content
19 高级类型

19 高级类型

•1 min•111 words

使用newtype模式 实现类型安全与抽象

newtype能静态地确保值不混淆:通过给相同的值封装成不同的newtpye

比如是u32类型封装一下成Millimeters 和 Meters,这样相同的值就有不同的单位,不会混淆。

newtype模式能封装、抽象内部类型的实现

新的类型可以暴露与其私有内部类型不同的共有 API。

类型同义词与类型别名

类型别名:给程序员看的语义语法

使用type 关键字为现有类型赋予另一个名字。类型别名的主要用途是减少重复。
在函数签名和类型注解中到处书写这个冗长的类型既乏味又容易出错。

没有返回 never type

有时候需要一个类型,表示“没有值”

Rust 有一个叫做! 的特殊类型。在类型理论术语中被称为 empty type,因为它没有值。我们更倾向于称之为 never 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! 返回的也是`!`类型,不然无法通过编译器。
        }
    }
}
Summary
  1. 循环中的 continue; break;
  2. 函数直接 reuturn;
  3. panic!()
  4. 没有出口的 loop 循环

循环永远也不结束,所以此表达式的值是 !。但是如果引入 break 这就不为真了

动态大小类型和Sized trait

Summary

因为要求栈帧的大小必须是确定的,所以动态大小类型无法将数据存储在栈上,但可以通过引用、指针等方式来操作动态大小类型的数据。

字符串类型,动态大小类型

因为字符串类型即可存放短字符串,又可存放长字符串,任意长度的字符串都可以。所以我们只能将字符串设置为动态大小类型。
str,也就是字符串类型,我们在栈上基本只能使用&str

trait object就是经典的例子

为了与trait区分,强制使用 dyn trait来表示 trait object类型,这个类型是动态类型,我们只能使用&dyn trait或者Box<dyn trait>

Sized trait与?Sized?

Rust 提供了  trait 来决定一个类型的大小是否在编译时可知

[!summary] Rust 提供了 Sized trait 来决定一个类型的大小是否在编译时可知
只有实现了Sized trait 的 类型才能在运行时被操作系统调用size_of::<T>()了获取到类型的内存大小。而动态类型则没有实现这个 trait。

引用不是类型,指针也不是类型,只有是类型,是类型

[!warning] 引用不是类型,指针也不是类型,只有&某类型是类型,Box<某类型>是类型
也就是&不能代表一个类型,Box<?>也不能代表一个类型,他们只是一个类型的组装部分。

泛型函数的限制:默认情况下泛型参数只能是Sized

默认情况下,类型参数从参数中提取,如果参数是引用或者指针,那么实际类型可能是固定大小也可能是动态大小类型。编译器默认认为是固定大小,会尝试获取类型大小分配在栈上。

通过?Sized 标注,让编译器放弃调用指针和引用的背后类型的大小

(让编译器不要追查真正的大小,直接使用引用和指针的大小即可。)
标注参数类型可能不是Sized之后,编译器只会尝试引用和指针本身分配在栈上,而背后的数据会直接安排分配在堆上。

Warning