通用概念
- 所谓类型,就是
对表示信息的值进行的细粒度的区分。 - 在类型系统中,一切皆类型。
- 基于类型定义的一些列组合、运算和转换等方法,可以看作类型的行为。
- 类型的行为决定了类型该如何计算,同时也是一种约束,保证信息被正确处理。
类型系统的分类
-
在
编译期进行类型检查的语言属于 静态语言。 -
在
运行期进行类型检查的语言属于 动态类型。 -
静态类型的语言能在编译期对代码进行静态分析,依靠的就是 类型系统。
类型系统与多态性
多态类型系统: 允许一段代码在不同的上下文中具有不同的类型的类型系统。多态性的好处: 可以在不影响类型丰富的前提下,为不同的类型编写通用的代码。
三种多态形式
- 参数化多态 --> 泛型
- Ad-hoc多态(特定多态) --> trait
- 子类型多态 --- (对rust来说)一种只存在于生命周期中的子类型多态
Rust类型系统概述
Rust 是一门 强类型,且类型安全的静态语言。
类型大小: Sized Type 、 Dynamic Sized Type (DST)、Zero Sized Type (ZST)
-
编程语言中不同的类型本质上是内存占用空间和编码方式的不同。
-
Rust 没有GC,编译器需要事先知道类型的大小,才能分配合理的内存。
-
Rust 中绝大部分类型都是在 编译期 可确定大小的类型, 即 固定大小类型(Sized Type)。例如, u32 是4字节,u64是8字节。
-
Rust 也有少量的 动态大小类型(Dynamic Sized Type, DST)。例如,str 类型的字符串字面量,对于编译器来说,编译器不可能事先知道会出现什么样的字符串字面量,所以,对编译器来说,str 类型的大小是无法确定的。
-
对 str 之类的类型来说,Rust 提供了引用类型。
-
&str 就是
引用类型,由指针和长度信息组成。 -
引用总会有固定的且在编译器已知的大小,是 Sized Type,编译器就可以正确地为其分配栈内存空间。 str 也会在运行时在堆上开辟空间。
-
胖指针(Fat Pointer):包含了动态大小类型
地址信息和长度信息的指针,叫做胖指针,&str 就是一种胖指针。// 展示指针的 fn main() { let s: &str = "Hello World!"; let ptr: *const u8 = s.as_ptr(); println!("s.as_ptr() = {:?}", ptr); // s.as_ptr() = 0x11000d917 println!("s.len() = {}", s.len()); // s.len() = 12 println!("s = {:p}", s); // s = 0x11000d917 println!("&&str = {:p}", &s); let s1 = String::from("aabb"); let s1_ref1 = &s1; let s1_ref2 = &s1; println!( "s1_ref1 point to (-->) {:p}, s1_ref2 --> {:p}", s1_ref1, s1_ref2 ); // point to same mem_addr, s1_ref1 point to (-->) 0x7ffee0819ca8, s1_ref2 --> 0x7ffee0819ca8 println!( "s1_ref1.len() = {}, s1_ref2.len() = {}", s1_ref1.len(), s1_ref2.len() ); // s1_ref1.len() = 4, s1_ref2.len() = 4 } -
与字符串切片同理,rust 中 数组 [T] 也是动态大小类型,编译器难以确定它的大小。因此定义数组,需要指明长度信息。
fn main() { let mut arr: [u32; 5] = [1, 2, 3, 4, 5]; println!("now array = {:?}", arr); { let mut_arr = &mut arr; reset(mut_arr); } println!("old array = {:?}", arr); } // 这里的参数必须是 固定大小类型。所以要么是 能确定大小的类型,要么是引用 // fn reset(arr: [u32]) { // this does not work ,定义数组必须指明大小。 fn reset(arr: &mut [u32; 5]) { arr[0] = 6; arr[1] = 5; arr[2] = 4; arr[3] = 3; // arr[4] = 2; // this is ok // arr[5] = 1; // this is error println!("reset array = {:?}", arr); }
零大小类型 (ZST, Zero Sized Type)
-
单元类型、单元结构体、空枚举,大小都是零。fn main() { println!("{}", std::mem::size_of::<Zenum>()); // 0 println!("{}", std::mem::size_of::<Number>()); // 1 println!("{}", std::mem::size_of::<Zstruct>()); // 0 println!("{}", std::mem::size_of::<Szero>()); // 0 println!("{}", std::mem::size_of::<[(); 30]>()); // 0 println!("{}", std::mem::size_of::<[Zstruct; 30]>()); // 0 } enum Zenum {} enum Number { Zero, One, Two, Three, } struct Zstruct; struct Szero { zstruct: Zstruct, zenum: Zenum, arr: [i32; 0], }
类型推导
-
Rust 只能在局部范围内进行类型推断。若遇到无法推断类型的地方,提前标明类型即可。
-
类型推断样例
fn sum(a: u32, b: i32) -> u32 { a + b as u32 } fn main() { let a = 1; // a is used in fn sum param 1, so a is type u32 let b = 2; // b is used in fn sum param 1, so b is type i32 println!("sum(a, b) = {}", sum(a, b)); let item = 3u8; let mut vec = vec![]; // vec is pushed item(u8), so vec type is Vec<u8> vec.push(item); println!("vec = {:?}", vec); } -
Turbofish 操作符
- 下面代码中,使用了 parse::<i32>() 这样的形式为泛型函数标注类型。
- 使用 ::<> 来标注类型的形式,叫做 turbofish 操作符
fn main() { let x = "1"; //let x1 = x.parse().unwrap(); // compile error let x1: i32 = x.parse().unwrap(); println!("x1 = {}", x1); let y = "3"; // let y1 = y.parse().unwrap(); // compile error let y1 = y.parse::<i32>().unwrap(); println!("y1 = {}", y1); }