Rust-高级类型

  Rust的类型系统还有一些其它功能。如 newtype类型别名(type aliases),一个类型似newtype但有首稍微不同的语义的功能。还有 ! 类型和动态大小类型。

为了类型安全和抽象而使用newtype模式

newtype模式可以用于一些其它我们还没讨论的功能,包括静态的确保某值不被混淆,和用来表示一个值的单元。

另一个newtype模式的应用在于抽象掉一些类型的实现细节:例如,封装类型可以暴露出出与直接使用其内部私有类型时所不同的公有api,以便限制其功能。

newtype也可以隐藏其内部的泛型类型。

类型别名用来创建类型同义词

连同newtype模式,Rust还提从了声明 类型别名(type alias)的能力,使用type关键字来给予现有类型另一个名。例如,可以像这样创建i32的别名 kilometers:

type kilometers = i32;

这意味首kilometers是i32的同义词(synonym)。

类型别名的主要用途是减少重复。例如,可能会有这样很长的类型:

 Box<dyn Fn() + send + 'static>

 在函数签名或类型注解中每次都书写这个类型将是易于出错的。

类型别加通过减少项目中重复代码的数量来使其更加易于控制。这里我们为这个冗长的类型引入了一个叫做Thunk的别名,这样就要以将所有使用这个类型的地方替换为更知的Thunk:

    type Chunk = Box<dyn Fn() + Send + 'static>;
    let f:  Chunk = Box::new(|| println!("hi"));

这样读写起来就容易多了。为类型别名选择一个好名字也可以帮助表达意图(单词thunk表示会在之后被计算的代码,所以这是一个存放闭包的合适的名字)。

从不返回的never type

Rust有一个叫做 ! 的特殊类型。在类型理论术语中,它被称为empty type,因为它没有值。我们更倾向于称之为never type。这个名字描述了它的作用:在函数从不返回的时候充当返回值。如:

fn bar() -> ! {
    ;
}

这读作“函数bar从不返回”,而从不返回的函数被称为 发散函数 (diverging functions)。不能创建!类型的值,所以bar也不可能返回值。

动态大小类型和Sized trait

因为Rust需要知道例如应该为特定类型的值分配多少空间这样的信息其类型系统的一个特定的角落可能令人迷惑:这就是 动态大小类型(dynamically sized types) 的概念。这有时被称为"DST"或"unsized types",这些类型允许我们处理只有在运行时才知道大小的类型。

str是一个DST,不是 &str; 直到运行时我们都不知道字符串有多少。因为直到运行时都不知道大小,也就意味着不能创建 str 类型的变量,也不能获取str类型的参数。以下这些代码不能工作:

    let s1:str = "Hello";
    let s2:str = "Hi";

Rust需要知道应该为特写类型的值分配多少内存,同时所有同一类型的值必须使用相同数据的内存。如果允许编写这样的代码,也就意味道这两个str需要占用完全相同大小的空间,不过它们有着不同的长度。这也就是为什么不可能创建一个存放动态大小类型的变量的原因。如果用 &str 是可行的。

所以虽然 &T 是一个储存了 T 所在的内存位置的单个值,&str 则是两个值:str的地址和其长度。这样,&str 就有了一个在编译时可以知道的大小:它是 usize 长度的两倍。也就是说,我们总是知道 &str 的大小,而无论其引用的字符串是多长。

这里是Rust中动态大小类型的常规用法:必须将动态大小类型的值置于某种指针之后。

可以将 str 与所有类型的指针结合:如合Box<str>或者Rc<str>。

为了处理DST,Rust有一个特定的trait来决定一个类型的大小是否在编译可知:这就是Sized trait。这个trait自动为编译器在编译时就知道大小的类型实现。另外,Rust隐式的为每一个泛型函数增加了Sized bound。也就是说,对于如下泛型函数定义:

fn generic<T>(t:T){

}

实际上被当作如下处理:

fn generic<T:Sized>(t:T){
}

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

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

?Sized traid bound 与 Sized 相对;也就是说,这可以读作“T可能是也可能不是Sized的”。这个语法只能用于Sized,而不能用于其它trait。

另外注意我们将 t 参数的类型从 T 变为了 &T:因为其类型可能不是Sized的,所以需要将其置于某种指针之后。

 

posted @ 2021-10-10 12:20  johnny_zhao  阅读(453)  评论(0编辑  收藏  举报