Rust中的泛型 Generics

87 阅读3分钟

结构体中使用泛型

结构体中的字段类型也可以用泛型来定义,下面代码定义了一个坐标点 Point,它可以存放任何类型的坐标值:

#[derive(Debug)]
struct Point<T> {
    x: T,
    y: T
}
fn main() {
    let integer = Point{x: 3, y: 5};
    let float = Point{ x: 2.4, y: 5.7};

    println!("{:?}", integer);
    println!("{:?}", float);
}

这里有两点需要特别的注意:

  • 提前声明,跟泛型函数定义类似,首先我们在使用泛型参数之前必需要进行声明 Point,接着就可以在结构体的字段类型中使用 T 来替代具体的类型
  • x 和 y 是相同的类型

枚举中使用泛型

enum Option<T> {
    Some(T),
    None,
}

Option 是一个拥有泛型 T 的枚举类型,它第一个成员是 Some(T),存放了一个类型为 T 的值。得益于泛型的引入,我们可以在任何一个需要返回值的函数中,去使用 Option 枚举类型来做为返回值,用于返回一个任意类型的值 Some(T),或者没有值 None。

对于枚举而言,卧龙凤雏永远是绕不过去的存在:如果是 Option 是卧龙,那么 Result 就一定是凤雏,得两者可得天下:

enum Result<T, E> {
    Ok(T),
    Err(E),
}

这个枚举和 Option 一样,主要用于函数返回值,与 Option 用于值的存在与否不同,Result 关注的主要是值的正确性。

如果函数正常运行,则最后返回一个 Ok(T),T 是函数具体的返回值类型,如果函数异常运行,则返回一个 Err(E),E 是错误类型。例如打开一个文件:如果成功打开文件,则返回 Ok(std::fs::File),因此 T 对应的是 std::fs::File 类型;而当打开文件时出现问题时,返回 Err(std::io::Error),E 对应的就是 std::io::Error 类型。

方法中使用泛型

struct Point<T>{
    x: T,
    y: T
}

impl<T> Point<T> {
    fn x(&self) -> &T{
        &self.x
    }
}


fn main() {
    let p1 = Point{x: 1, y: 2};
    println!("{}", p1.x());
}

使用泛型参数前,依然需要提前声明:impl,只有提前声明了,我们才能在Point中使用它,这样 Rust 就知道 Point 的尖括号中的类型是泛型而不是具体类型。需要注意的是,这里的 Point 不再是泛型声明,而是一个完整的结构体类型,因为我们定义的结构体就是 Point 而不再是 Point。

除了结构体中的泛型参数,我们还能在该结构体的方法中定义额外的泛型参数,就跟泛型函数一样:

struct Point<T, U>{
    x: T,
    y: U
}

impl<T, U> Point<T, U> {
    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: 1, y: 1.0};
    let p2 = Point{x: "Hello", y: "World"};

    let p3 = p1.mixup(p2);
    println!("{}, {}", p3.x, p3.y);
}

const 泛型

在之前的泛型中,可以抽象为一句话:针对类型实现的泛型,所有的泛型都是为了抽象不同的类型,那有没有针对值的泛型?

const 泛型,也就是针对值的泛型,正好可以用于处理数组长度的问题:

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

    let arr: [i32; 2] = [1, 2];
    display_array(arr);
}

如上所示,我们定义了一个类型为 [T; N] 的数组,其中 T 是一个基于类型的泛型参数,这个和之前讲的泛型没有区别,而重点在于 N 这个泛型参数,它是一个基于值的泛型参数!因为它用来替代的是数组的长度。

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

在泛型参数之前,Rust 完全不适合复杂矩阵的运算,自从有了 const 泛型,一切即将改变。