rust 泛型

Created

2024-05-04 14:34:17.340767944

Updated

2024-10-27 17:19:25

1 在函数中使用

fn largest_i32(list: &[i32]) -> &i32 {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn largest_char(list: &[char]) -> &char {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest_i32(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest_char(&char_list);
    println!("The largest char is {}", result);
}

我们发现 同样的逻辑代码,我们写了2遍, 很麻烦,重复代码, 使用泛型来作为具体类型的抽象

泛型是一种类型,在函数中使用它作为参数的类型,是需要申明的,首先肯定不会和参数的申明放在一块,所以rust在函数名和参数列表之间使用<T>,一般用T来作为泛型标识

// T: std::cmp::PartialOrd
// 这里的代码 运行会提示错误, 很显然 不是所有的类型都能做比较大小的操作的
// 将T改成 T: std::cmp::PartialOrd 就ok了, 后面trait 里会说
fn largest<T>(list: &[T]) -> &T {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    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 largest char is {}", result);
}

2 在结构体中使用

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

fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
}

使用多个泛型

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

fn main() {
    let both_integer = Point { x: 5, y: 10 };
    let both_float = Point { x: 1.0, y: 4.0 };
    // 这样你随便写啥都行
    let integer_and_float = Point { x: 5, y: 4.0 };
}

3 在枚举中使用

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

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

fn main() {
    let some_value = Option::Some(5);
    let none_value = Option::None;

    let integer_value = Result::Ok(5);
    let error_value = Result::Err(5);
}

4 impl中使用

struct Point<T> {
    x: T,
    y: T,
}
// 在impl 后<T> 是申明泛型参数 T
// 来表明是为  Point<T> 这个类型实现方法,这里的T就使用了前面申明的T
impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}
// 可以为 某个具体的类型实现方法
// 因为上面的泛型包含了这种情况, 所以同时存在会报错
// impl Point<i32> {
//     fn x(&self) -> &i32 {
//         &self.x
//     }
// }
fn main() {
    let p = Point { x: 5, y: 10 };

    println!("p.x = {}", p.x());
}

方法与结构体使用不同的泛型

struct Point<X1, Y1> {
    x: X1,
    y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
    // 这里mixup后申明的X2,Y2,表示 方法里使用了X2,Y2泛型,与Point<X1, Y1>中的X1,Y1不一样
    fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c' };

    let p3 = p1.mixup(p2);

    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

5 ::<T>

fn main() {
    // 声明一个类型为 Vec<i32> 的变量,并初始化它
    let numbers: Vec<i32> = Vec::new();

    // 泛型函数
    fn create_empty_vec<T>() -> Vec<T> {
        Vec::new()
    }

    let empty_int_vec = create_empty_vec::<i32>();
    let empty_int_vec2: Vec<i32> = create_empty_vec();
}
trait Hello {
    fn world(&self);
}
impl<T> Hello for Vec<T> {
    fn world(&self) {
        println!("hello");
    }
}
fn main() {
    // 声明一个类型为 Vec<i32> 的变量,并初始化它
    let numbers: Vec<i32> = Vec::new();
    numbers.world();
    // Vec::<i32> 表示元素是i32类型的 Vec
    Vec::<i32>::world(&numbers);
}

6 性能问题

Tip

其实泛型相当于占位符,然后把你编写的代码理解成模板, 编译器编译的时候,会将这些占位符替换成具体的类型, 比如上面的例子, 编译器会根据你实际使用的情况,将T替换成具体的类型比如 i32,f64,会生成对应的代码, 这个叫单态化, 这样运行时不会有额外的开销

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

impl Point<f64> {
    fn x(&self) -> &f64 {
        &self.x
    }
}
Back to top