rust学习 -- 第十章 泛型

110 阅读4分钟

第十章 泛型

消除重复的代码

消除重复的步骤

  1. 识别重复的代码
  2. 提取重复的代码到函数体中, 并在函数签名中指定函数的输入和返回值
  3. 将重复的代码使用函数调用进行替换

泛型的功能

泛型: 提高代码的复用能力 -------处理重复代码的问题.

泛型是具体类型或其他属性的抽象代替:

  • 使用泛型编写的代码不是最终的代码, 而是一种模板, 里面有一些“占位符”.
  • 编译器在编译时将 “占位符” 替换为具体的类型.
fn largest<T>(list: &[T]) -> T {...}

如上例所示 类型的参数通常是一个大写字母, 通常使用T来代替. 首先 largest<T> 对泛型参数 T 进行了声明, 然后才在函数参数中进行使用该泛型参数 list: &[T].

泛型定义

函数定义中使用泛型

fn largest<T>(list: &[T]) -> T { // 使用泛型代替函数签名
    let mut largest = list[0];
    for &item in list {
        if item > largest {  // 这里会报错,这里只是表达我们可以这样实现, 为了消除错误,我们需要指定T实现std::cmp::PartialOrd的Trait, 后面修改
            largest = item;
        }
    }
    largest
}
​
fn main() {
    let number_list = [32, 56, 23, 63, 220];
    let result = largest(&number_list);
    println!("The largest number is {}", result);
​
    let char_list = vec!['y', 'm', 'a', 'q'];
    let result = largest(&char_list);
    println!("The larest char is {}", result); 
}

在声明一个函数的时候,我们还可以不指定具体的参数或返回值的类型,而是由泛型参数来代替,(传进什么类型,传出什么类型).

fn id<T>(x:T) -> T {  // 泛型函数
    return x;
}
​
fn main() {
    let int = id(0);
    let string = id("Try");
    println!("{}, {}", int, string);
}

Rust 会对泛型函数进行单态化处理.也就是在编译时,把所有用到的泛型函数的泛型参数展开,生成若干个函数.

结构体中的泛型

结构体中可以为每个成员都指定类型. 所以也可以精准的控制每个成员的泛型

struct Point<T> {
    x: T,
    y: T, 
}
​
fn main() {
    let integer = Point {x:5, y:7};
    let string = Point {x:'a', y:'b'};  // 此时x,y 是同一种类型的
}
struct Point<T, U> {
    x: T,
    y: U, 
}
​
fn main() {
    let integer = Point {x:5, y:'c'}; // 此时x,y 可以是不同类型的
}

枚举中使用泛型

enum Option<T> {
    Some(T),
    None,
}
​
enum Result<T, E> {
    Ok(T),
    Err(E),
}

方法的定义中使用泛型

struct Point<T> {
    x:T,
    y:T,
}
​
impl<T> Point<T> {  // 此方法所有类型都有
    fn x(&self) -> &T {
        &self.x
    }
}
​
impl Point<i32> {  // 只有在i32的类型上才有方法x1
    fn x1(&self) -> &i32 {
        &self.x
    }
}

将T 放在impl关键字后, 表示在类型T上实现方法.指定具体类型后就是对具体类型实现的方法,其他类型不可以调用.

struct 中的泛型参数可以和方法的泛型类型参数不同

struct Point<T, U> {
    x:T,
    y:U,
}
​
impl<T, U> Point<T, U> {
    // 此处的方法的泛型和struct中泛型类型就不同. 但是我认为这里主要是因为输入了一个新的不同类型的对象,并且要使用新对象里的泛型,所以才使用新的泛型.
    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}
​
fn main() {
    let p1 = Point { x:5, y: 4};
    let p2 = Point { x: "Hello", y: 'c' };
    let p3 = p1.mixup(p2);
​
    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

const 泛型(Rust 1.51 版本引入的重要特性)

针对类型实现的泛型, 所有的泛型都是为了抽象不同的类型, 那有没有针对值的泛型?

下面的代码中 [i32; 2], [i32; 3]是两个不同的类型:

fn display_array(arr: [i32; 3]) {
    println!("{:?}", arr);
}
fn main() {
    let arr: [i32; 3] = [1, 2, 3];
    display_array(arr);
​
    let arr: [i32;2] = [1,2];  // 类型不匹配
    display_array(arr);
}

可以使用引用的方式进行修改.

fn display_array(arr: &[i32]) {  // 可以是任意长度的数组
// fn display_array<T: std::fmt::Debug>(arr: &[T]) {  // 使用泛型代替特定类型
    println!("{:?}", arr);
}
fn main() {
    let arr: [i32; 3] = [1, 2, 3];
    display_array(&arr);
​
    let arr: [i32;2] = [1,2];
    display_array(&arr);
}

某些情况下不能使用引用, 这时需要使用 值的泛型.

fn display_array<T: std::fmt::Debug, const N: usize>(arr: [T; N]) {  // N就是泛型参数
    println!("{:?}", arr);
}
fn main() {
    let arr: [i32; 3] = [1, 2, 3];
    display_array(arr);
​
    let arr: [i32; 2] = [1, 2];
    display_array(arr);
}

const 泛型定义的语法是 const N: usize,表示 const 泛型 N ,它基于的值类型是 usize