笔记-Rust类型系统2: 泛型 和 trait

186 阅读2分钟

泛型 Generics

泛型是一种参数化多态。

可以编写类型抽象的代码,减少重复代码。

1. 泛型类型 --> 泛型结构体 和 泛型枚举

Box<T>, Option<T>, Result<T, E> 都是泛型类型。

fn main() {
    let aa: Box<&str> = Box::new("aadd");
    let bb: Option<i32> = Option::Some(67);
    let cc: Result<String, &str> = Result::Ok("101aa".to_string());

    // unwrap box by *
    let mut dd = String::from(*aa);
    dd.push_str("ee");
    println!("dd = {}", dd);

    // unwrap Some<T> by "if let" or unwrap()
    if let Some(bb_i1) = bb {
        println!("bb_i1 = {}", bb_i1);
    }
    let bb_i2: i32 = bb.unwrap();
    println!("bb.unwrap() = {}", bb.unwrap());

    // unwrap Result<T, E> by unwrap()
    let ss: String = cc.unwrap();
    println!("cc.unwrap() = {}", ss);
}

2. 泛型函数

泛型也可应用于函数,定义泛型函数。

2.1 定义泛型函数:

fn main() {
    let a: i32 = foo(1);
    let b: &str = foo("hello");
    println!("a = {}, b = {}", a, b); // a = 1, b = hello
}

// 泛型函数: 函数方法名称的后面加上 <T>, <A, B>, <A, B, C> 等需要定义的泛型类型
fn foo<T>(x: T) -> T {
    return x;
}

2.2 为泛型结构体实现具体方法:

  • 与枚举和函数一样,结构体名称后面的 <T> 叫做 泛型声明
  • 泛型只有被声明后才可以被使用。
  • Rust中的泛型属于静多态,是一种编译器多态,会被 单态化
  • 单态化意味着,对于上面的foo函数, fn foo<T>(x: T) -> T,编译器要将一个泛型函数生成两个具体类型对应的函数。
#[derive(Debug, PartialEq, Eq)]
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn new(a: T, b: T) -> Self {
        Point { x: a, y: b }
    }
}

fn main() {
    let p1: Point<i32> = Point::new(1, 2);
    let p2: Point<&str> = Point::new("1", "2");

    assert_eq!(p1, Point { x: 1, y: 2 });
    assert_eq!(p2, Point { x: "1", y: "2" });
}
  • 单态化静态分发的 好处 是性能好,没有运行时开销
  • 单态化静态分发的 坏处 是容易造成编译后生成的二进制文件膨胀

2.3 泛型返回值自动推导

编译器还可以对返回值进行自动推导。

#[derive(Debug)]
struct Sa(i32);

#[derive(Debug)]
struct Sd(i32, i32);

trait Inst {
    fn new(i: i32) -> Self;
}

impl Inst for Sa {
    fn new(i: i32) -> Self {
        Sa(i)
    }
}

impl Inst for Sd {
    fn new(i: i32) -> Self {
        Sd(i, i + 10)
    }
}

fn try_new<T: Inst>(i: i32) -> T {
    T::new(i)
}

fn main() {
    let aa: Sa = try_new(1); // 根据声明的变量aa的类型Sa,推导出:需要调用返回值类型为 Sa 的 try_new 函数
    let dd: Sd = try_new(2);
    println!("aa = {:?}", aa);
    println!("dd = {:?}", dd);
}