正式开端

  1. 在机器码的世界中,指令仅仅和立即数或许内存打交道,内存中寄存的数据都是字节省
  2. 类型体系完满是一种工具

类型体系基本概念与分类

  1. 类型:是对值的区别,它包含了值在内存中的长度、对齐以及值能够进行的操作等信息
  2. 类型体系其实便是,对类型进行界说、查看和处理的体系

分类

  1. 按界说后类型是否能够隐式转化,能够分为强类型和弱类型

  2. 按类型查看的机遇,在编译时查看仍是运行时查看,能够分为静态类型体系和动态类型体系

    a. 对于静态类型体系,还能够进一步分为显式静态和隐式静态

多态

  1. 在运用相同的接口时,不同类型的对象,会采用不同的完成

  2. 对于动态类型体系,多态经过**鸭子类型(duck typing)**完成

  3. 对于静态类型体系,多态能够经过**参数多态(parametric polymorphism)、特设多态(adhoc polymorphism)和子类型多态(subtype polymorphism)**完成

    a. 参数多态是指,代码操作的类型是一个满意某些束缚的参数,而非具体的类型。

    b. 特设多态是指同一种行为有多个不同完成的多态。比如加法,能够 1+1,也能够是 “abc” + “cde”、matrix1 + matrix2、甚至 matrix1 + vector1。在面向对象编程言语中,特设多态一般指函数的重载

    c. 子类型多态是指,在运行时,子类型能够被当成父类型运用

  4. 在 Rust 中,参数多态经过泛型来支撑、特设多态经过 trait 来支撑、子类型多态能够用 trait object 来支撑

12|类型系统:Rust的类型系统有什么特点?

Rust类型体系

  1. 在界说时, Rust 不答应类型的隐式转化,也便是说,Rust 是强类型言语;
  2. 在查看时,Rust 运用了静态类型体系,在编译期确保类型的正确
  3. 从内存的视点看,类型安满是指 代码只能按照被答应的办法和被答应的权限,拜访它被授权拜访的内存

Rust 中除了 let / fn / static / const 这些界说性句子外都是表达式,而一切表达式都有类型

在 Rust 中,对于一个效果域,无论是 if / else / for 循环仍是函数,最终一个表达式的回来值便是效果域的回来值,假如表达式或许函数不回来任何值,那么它回来一个 unit() 。unit 是只要一个值的类型,它的值和类型都是 ()

数据类型

  1. Rust 的原生类型包括字符、整数、浮点数、布尔值、数组(array)、元组(tuple)、切片(slice)、指针、引证、函数等

12|类型系统:Rust的类型系统有什么特点?

  1. 在原生类型的基础上,Rust 标准库还支撑非常丰富的组合类型

12|类型系统:Rust的类型系统有什么特点?

  1. 类型分类

12|类型系统:Rust的类型系统有什么特点?

类型推导

  1. 在一个效果域之内,Rust 能够根据变量运用的上下文,推导出变量的类型
  2. collect 是 Iterator trait 的办法,它把一个 iterator 转化成一个调集
  3. 在泛型函数后运用 ::来强制运用类型 T,这种写法被称为 turbofish
  4. 有些情况下,即便上下文中含有类型的信息,也需求开发者为变量供给类型,比如常量和静态变量的界说

用泛型完成参数多态

泛型数据结构

泛型结构 Vec<T>

pub struct Vec<T, A: Allocator = Global> {
    buf: RawVec<T, A>,
    len: usize,
}
pub struct RawVec<T, A: Allocator = Global> {
    ptr: Unique<T>,
    cap: usize,
    alloc: A,
}
/*
Vec有两个参数
一个是 T,是列表里的每个数据的类型
另一个是 A,它有进一步的束缚 A: Allocator ,也便是说 A 需求满意 Allocator trait
A 这个参数有默许值 Global,它是 Rust 默许的大局分配器
*/

枚举类型 Cow

生命周期标注也是泛型的一部分,一个生命周期 ‘a 代表恣意的生命周期,和 T 代表恣意类型是相同的

/*
这儿对B 的三个束缚分别是:
1. 生命周期 'a
2. 长度可变 ?Sized
3. 符合 ToOwned trait
*/
pub enum Cow<'a, B: ?Sized + 'a> where B: ToOwned,
{
    // 借用的数据
    Borrowed(&'a B),
    // 具有的数据
    // 对 B 做了一个强制类型转化,转成 ToOwned trait
    // 然后拜访 ToOwned trait 内部的 Owned 类型
    Owned(<B as ToOwned>::Owned),
}

意义

Cow(Clone-on-Write)在回来数据的时候,供给了一种可能:

  1. 要么回来一个借用的数据(只读)
  2. 要么回来一个具有一切权的数据(可写)

Cow 泛型束缚参数

  1. 对于具有一切权的数据 B ,第一个是生命周期束缚。

    a. 这儿 B 的生命周期是 ‘a,所以 B 需求满意 ‘a,用 B: ‘a 来标明。

    b.当 Cow 内部的类型 B 生命周期为 ‘a 时,Cow 自己的生命周期也是 ‘a。

  2. B 还有两个束缚:?Sized 和 “where B: ToOwned”

    a. ?Sized 是一种特殊的束缚写法,? 代表能够放松问号之后的束缚。

    b. 因为 Rust 默许的泛型参数都需求是 Sized,也便是固定巨细的类型,所以这儿 ?Sized 代表用可变巨细的类型

    c. ToOwned 是一个 trait,它能够把借用的数据克隆出一个具有一切权的数据

泛型束缚的方式

在表述泛型参数的束缚时,Rust 答应两种方式

  1. 一种类似函数参数的类型声明,用 “:” 来标明束缚,多个束缚之间用 + 来标明;
  2. 另一种是运用 where 子句,在界说的结束来标明参数的束缚
  3. 两种办法都能够,且能够共存

泛型函数

  1. 在声明一个函数的时候,我们能够不指定具体的参数或回来值的类型,而是由泛型参数来替代

  2. 对于泛型函数,Rust 会进行单态化(Monomorphization)处理,也便是在编译时,把一切用到的泛型函数的泛型参数展开,生成若干个函数

    a. 单态化的优点是,泛型函数的调用是静态分派

    b. 编译速度很慢,一个泛型函数,编译器需求找到一切用到的不同类型,一个个编译

小结

12|类型系统:Rust的类型系统有什么特点?

好用链接

  1. 鸭子类型 概念
  2. 参数多态 概念
  3. 特设多态 概念
  4. 子类型多态
  5. Rust 原生类型
  6. Iterator trait
  7. 内存分配 Global
  8. Rust 默许的大局分配器
  9. ToOwned trait
  10. 单态化 概念
  11. 里氏替换准则
  12. 各个言语完成和处理泛型

精选问答

  1. 下面这段代码为什么不能编译经过?你能够修正它使其正常作业么?

use std::io::{BufWriter, Write};
use std::net::TcpStream;
#[derive(Debug)]
struct MyWriter<W> {
    writer: W,
}
impl<W: Write> MyWriter<W> {
    pub fn new(addr: &str) -> Self {
        let stream = TcpStream::connect("127.0.0.1:8080").unwrap();
        Self {
            writer: BufWriter::new(stream),
        }
    }
    pub fn write(&mut self, buf: &str) -> std::io::Result<()> {
        self.writer.write_all(buf.as_bytes())
    }
}
fn main() {
    let writer = MyWriter::new("127.0.0.1:8080");
    writer.write("hello world!");
}

代码报错的主要原因是

  1. 完成 new 办法时,对泛型的束缚要求要满意 W: Write,而 new 的声明回来值是 Self,也便是说 self.wirter 有必要是 W: Write 类型(泛型)
  2. 但实际回来值是一个确认的类型 BufWriter<TcpStream>,这不满意要求

修正办法有这么几个思路

  1. 修正 new 办法的回来值
impl<W: Write> MyWriter<W> {
    pub fn new(addr: &str) -> MyWriter<BufWriter<TcpStream>> { 
        let stream = TcpStream::connect(addr).unwrap(); 
        MyWriter {
            writer: BufWriter::new(stream), 
        } 
    }
} 
fn main() { 
    let mut writer = MyWriter::<BufWriter<TcpStream>>::new("127.0.0.1:8080"); 
    writer.write("hello world!"); 
}
  1. 对确认的类型 MyWriter<BufWriter>完成 new 办法
impl MyWriter<BufWriter<TcpStream>> {
    pub fn new(addr: &str) -> Self { 
        let stream = TcpStream::connect(addr).unwrap(); 
        Self {
            writer: BufWriter::new(stream), 
        }
    } 
}
fn main() { 
    let mut writer = MyWriter::new("127.0.0.1:8080"); 
    writer.write("hello world!"); 
}
  1. 修正 new 办法的完成,运用依靠注入
impl<W: Write> MyWriter<W> {
    pub fn new(writer: W) -> Self { 
        Self {
            writer, 
        }
    }
} 
fn main() { 
    let stream = TcpStream::connect("127.0.0.1:8080").unwrap(); 
    let mut writer = MyWriter::new(BufWriter::new(stream)); 
    writer.write("hello world!"); 
}