Rust Generic

Table of Contents

1. Rust Generic

1.1. scenario

1.1.1. struct

#![feature(core_intrinsics)]
fn type_of<T>(_: &T) -> &str {
    unsafe { std::intrinsics::type_name::<T>() }
}

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

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

fn main() {
    let p = Point { x: 0, y: 0 };
    let p2 = Point2 { x: 0, y: 0f64 };

    println!("{:?} {:?}", type_of(&p), type_of(&p2));
}
"d41d8cd98f::Point<i32>" "d41d8cd98f::Point2<i32, f64>"

1.1.2. enum

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

1.1.3. function

fn nop<T>(x: T) -> () {
    let y = x;
}

1.1.4. method

1.1.4.1. method 自身有泛型参数
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn nop<T>(&self, x: T) -> () {}
}

fn main() {
    let p = Point { x: 0, y: 0 };
    p.nop(0);
}
1.1.4.2. method 使用 struct 的泛型参数
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn nop(&self, x: T) -> () {}
}

fn main() {
    let p = Point { x: 0, y: 0 };
    p.nop(10);
}
1.1.4.3. impl<T> 与 impl
struct Point<T> {
    x:T,
    y:T,
}

impl Point<i32> {
    fn sum (&self) -> i32 {
        self.x + self.y
    }
}
// impl<T> 是为了区别 Point<T> 与 Point<i32>

impl<T> Point<T> {
    fn nop (&self) -> () {

    }
}

1.1.5. trait

trait 与 struct 一样也支持泛型参数

trait Sum<T> {
    fn sum(self) -> T;
}

struct Point {
    x: i32,
    y: i32,
}

impl Sum<i32> for Point {
    fn sum(self) -> i32 {
        self.x + self.y
    }
}

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

1.1.6. lifetime

1.2. trait bounds

针对 method 和 function 的模板的特化 (specialization) 有两种方式:

  1. 直接指定具体的类型 (concrete type)

    struct Point<T> {
        x: T,
        y: T,
    }
    
    impl Point<i32> {
        fn dump(&self) {
            println!("{:?}", (self.x, self.y));
        }
    }
    
    fn main() {
        let p = Point { x: 1, y: 2 };
        p.dump();
    }
    
  2. trait bounds

    struct Point<T> {
        x: T,
        y: T,
    }
    
    impl<T> Point<T> {
        fn dump(&self) {
            println!("{:?}", (self.x, self.y));
        }
    }
    
    fn main() {
        let p = Point { x: 1, y: 2 };
        println!("{:?}", p.dump());
    }
    // error[E0277]: `T` doesn't implement `std::fmt::Debug`
    // --> quickrun:8:26
    // |
    // 8 |         println!("{:?}", (self.x, self.y));
    // |                          ^^^^^^^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
    // |
    // = help: the trait `std::fmt::Debug` is not implemented for `T`
    // = help: consider adding a `where T: std::fmt::Debug` bound
    // = note: required because of the requirements on the impl of `std::fmt::Debug` for `(T, T)`
    // = note: required by `std::fmt::Debug::fmt`
    

    我们希望把 dump 写的通用一些, 能自动 specialize 成不同的类型, 但编译错误.

    因为编译器需要确保 T 类型可能被打印. 正确的作法是通过 trait bound 限定T 需要具体的能力

    struct Point<T> {
        x: T,
        y: T,
    }
    // std::fmt::Debug + Copy 表示 T 类型需要 impl 两个 trait:
    // 1. Debug, 用来 print
    // 2. Copy, 因为 self 是引用, self.x 无法被 move out of self
    impl<T: std::fmt::Debug + Copy> Point<T> {
        fn dump(&self) {
            println!("{:?}", (self.x, self.y));
        }
    }
    
    // 也可以把 trait bounds 写在 where 中
    impl<T> Point<T>
    where
        T: std::fmt::Debug + Copy,
    {
        fn dump_reverse(&self) {
            println!("{:?}", (self.y, self.x));
        }
    }
    
    fn main() {
        let p = Point { x: 1, y: 2 };
        p.dump();
        p.dump_reverse();
    }
    

1.2.1. 函数自身泛型参数的 traits bound

traits bound 除了可以限制 struct 的泛型参数, 也可以用来限制函数自身的泛型参数

fn larger<T>(x: T, y: T) -> T
where
    T: std::cmp::PartialOrd,
{
    if x >= y {
        return x;
    }
    return y;
}

fn main() {
    println!("{:?}", larger(1, 2));
    println!("{:?}", larger("abc", "abd"));
}
1.2.1.1. impl trait

`impl trait` 是一种针对 trait bound 的语法糖

use core::fmt::Debug;

fn dump(x: impl Debug) {
    println!("{:?}", x);
}

fn dump2<T>(x: T)
where
    T: Debug,
{
    println!("{:?}", x);
}

fn main() {
    dump(1);
    dump2(1);
}

1.3. associate types

对于 trait 来说, 泛型参数可以改写为 associate type 的形式

trait Sum {
    type T;
    fn sum(self) -> Self::T;
}

struct Point {
    x: i32,
    y: i32,
}

impl Sum for Point {
    type T = i32;
    fn sum(self) -> i32 {
        self.x + self.y
    }
}

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

由于 associate type 实际相当于原来的泛型参数, 所以需要使用 Sum trait 时需要指明 associate type, 例如:

fn foo<S>(x: S) -> ()
where
    S: Sum<T = i32>,
{

}

Author: [email protected]
Date: 2018-12-28 Fri 00:00
Last updated: 2024-08-06 Tue 16:22

知识共享许可协议