笔记-Rust类型系统1:概述

375 阅读4分钟

通用概念

  • 所谓类型,就是对表示信息的值进行的细粒度的区分
  • 在类型系统中,一切皆类型。
  • 基于类型定义的一些列组合、运算和转换等方法,可以看作类型的行为。
  • 类型的行为决定了类型该如何计算,同时也是一种约束,保证信息被正确处理。

类型系统的分类

  • 编译期 进行类型检查的语言属于 静态语言。

  • 运行期 进行类型检查的语言属于 动态类型。

  • 静态类型的语言能在编译期对代码进行静态分析,依靠的就是 类型系统。

类型系统与多态性

  • 多态类型系统: 允许一段代码在不同的上下文中具有不同的类型的类型系统。
  • 多态性的好处: 可以在不影响类型丰富的前提下,为不同的类型编写通用的代码。

三种多态形式

  • 参数化多态 --> 泛型
  • 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);
    }