newtype模式

除了高级trait中用到的newtype模式外,newtype还可以用于抽象一些类型的实现细节,例如封装类型暴露出与内置内容不同的共有API,限制其功能。

隐藏内部的泛型类型。比如提供一个封装了HashMap<i32, String>People类型。这样用户只是用People的公共API,不知道底层类型。

类型别名

通过type关键字可以获得给与现有类型另一个别名的。别名不是新的类型,而是完全被当作被起别名的类型处理。与c++中的type和using作用类型。

类型别名的主要用途是减少重复。当类型很长的时候非常有用。并且更加具有意义。

就像std::io::Error中函数但部分会返回Result<..., std::io::Error>错误一样,所以也取了一个别名

type Result<T> = std::result::Result<T, std::io::Error>;

这样使用的时候只用Result<T>就足够了。

从不返回的never type

Rus中一个叫做!的特殊类型。它没有值,称为newver type。在函数从不返回的时候充当返回值。例如:

fn bar() -> ! {
    // --snip--
    panic!();
}

描述 ! 的行为的正式方式是 never type 可以强转为任何其他类型。在match表达式中,要求match的分支必须返回相同的类型,但是存在下面的情况:

let guess: u32 = match guess.trim().parse() {
	Ok(num) => num,
	Err(_) => continue,
};
 
impl<T> Option<T> {
    pub fn unwrap(self) -> T {
        match self {
            Some(val) => val,
            None => panic!("called `Option::unwrap()` on a `None` value"),
        }
    }
}

continuepanic!,Rust在计算类型的时候会将它们当作!类型,所以可以编译通过,但是实际上,它们并不产生值,而是控制程序的流程,比如continue将控制权移交给上层循环,panic!终止程序。

动态大小类型与Sized trait

Rust 需要知道应该为特定类型的值分配多少内存,同时所有同一类型的值必须使用相同数量的内存。但是Rust中是有动态大小类型的,被称为DST或者unsized types。只有在运行的时候才知道大小的类型。

str这个动态大小类型进行说明。Rust中是无法创建str,对于这种类型的处理器,Rust是创建了其引用&str的,因为&str的大小是确定的。

Rust 中动态大小类型的常规用法:他们有一些额外的元信息来储存动态信息的大小。这引出了动态大小类型的黄金规则:必须将动态大小类型的值置于某种指针之后

比如 Box<str> 或 Rc<str>

为了处理 DST,Rust 有一个特定的 trait 来决定一个类型的大小是否在编译时可知:这就是 Sized trait。能够在编译期就确定大小的类型都实现了这个trait。

Rust 隐式的为每一个泛型函数增加了 Sized bound。也就是说,对于如下泛型函数定义:

fn generic<T>(t: T) {
    // --snip--
}
 
// 等价于
fn generic<T: Sized>(t: T) {
    // --snip--
}

泛型函数默认只能用于在编译时已知大小的类型。然而可以使用如下特殊语法来放宽这个限制:

fn generic<T: ?Sized>(t: &T) {
    // --snip--
}

?Sized这个写法只能用于Sized这个trait,会覆盖默认泛型参数拥有固定大小的默认规则。因为T类型可能不是固定大小的,所以需要置于某种指针之后,可以看到函数的参数由T变为了&T