美文网首页
rust泛型编程范式

rust泛型编程范式

作者: Wu杰语 | 来源:发表于2024-06-26 20:31 被阅读0次

    1 泛型范式

    C++、JAVA泛型范式有非常广泛的应用,也即模版方法和模版类,我们使用非常熟悉。
    模版方法和模版类会在编译期间具化,在rust中叫单态化,将模版结构、方法、trait按照具体的类型单态具化为若干拷贝代码。

    • 好处:模版方法和模版类在编译期间,把所有用到的泛型函数的泛型参数展开,生成若干个函数,这样就和普通函数一样,没有任何效率损失。
    • 缺点:编译器需要找到所有用到的不同类型,一个个编译,编译速度会减慢 ,编出来的二进制会比较大,因为泛型函数的二进制代码实际存在 N 份。

    Rust的是一种静态强类型语言,其多态有参数多态、特型多态、子类型多态。

    • 参数多态是指,代码操作的类型是一个满足某些约束的参数,而非具体的类型。
    • 特设多态是指同一种行为有多个不同实现的多态。
    • 子类型多态是指,在运行时,子类型可以被当成父类型使用。

    2. 参数多态和特型多态

    参数多态和特型通过结构体、函数、trait三种语法场景表现出来。

    2.1 结构体多态

    结构体多态是对相同的数据结构行为的抽象,在rust中也比较常见,例如

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

    2.2 函数多态

    函数多态也即模版方法, 可以直接进行限定,或者用where语句进行限定(限定 英文是Bound(边界)是一个用于指定泛型类型参数的限制条件

    fn print_value<T: std::fmt::Display>(value: T) 
    fn print_value<T>(value: T) where T: std::fmt::Display
    

    也可以用+进行多重限定

    fn print_value<T: Debug + Display>(value: T) 
    

    另外利用impl,可以进行参数或者返回值约束(impl的好处是解决限定是组合的情况)(注意看下面的注释处,可以返回Itrerator<item=i32>、Box<Itrerator<item=i32>>,甚至更长的组合,这里我们用impl Itrerator<item=i32>就可以解决问题了)

    fn combine_vecs<T: (
        v: Vec<i32>,
        u: Vec<i32>,
    ) -> imp Iterator<Item=i32> {
              // 注意这里可以返回实现了Itrerator<item=i32>的结构,也可以返回满足Box<Itrerator<item=i32>>的结构
    }
    

    2.3 trait多态

    一个典型的triat模版如下

    pub trait Add<Rhs = Self> {
        type Output;
    
        fn add(self, rhs: Rhs) -> Self::Output;
    }
    

    对于trait,有Self、子类型、关联类型几个特性要特别了解

    1. trait多态,第一个要理解的是Self,Self也即结构本身,例如Clone的返回值,Self返回的就是实现了CloneTrait的结构本身,而函数中&self实际是 self: &Self的简写,&mut self是self: &mut Self的简写。
    pub trait Clone {
        fn clone(&self) -> Self;
        fn clone_from(&mut self, source: &Self) { ... }
    }
    
    1. trait也有子类型,例如
    trait MyTrait : Debug {
    }
    

    这里如果结构体实现MyTrait,也必须同时实现Debug的方法,否则编译器会报错。

    1. trait的关联类型,如下:
    pub trait Iterator {
        type Item;
        fn next(&mut self) -> Option<Self::Item>;
    }
    

    迭代器trait中的Item就是关联类型,实现一个迭代器例子如下:

    //(来自标准库中std::env模块的代码)
    impl Iterator for Args {
        type Item = String;
    
        fn next(&mut self) -> Option<String> {
            ...
        }
        ...
    }
    

    3. trait object

    如下例子,&dyn trait或者Box<dyn trait>,实现了子类型多态。

    trait Animal {
        fn speak(&self);
    }
    
    struct Dog;
    
    impl Animal for Dog {
        fn speak(&self) {
            println!("Woof!");
        }
    }
    
    struct Cat;
    
    impl Animal for Cat {
        fn speak(&self) {
            println!("Meow!");
        }
    }
    
    // 等价于 fn name<T: Animal>(animal: T) -> &'static str;
    fn name(animal: impl Animal) -> &'static str {
        animal.name()
    }
    
    fn main() {
        let animals: Vec<Box<dyn Animal>> = vec![
            Box::new(Dog),
            Box::new(Cat),
        ];
    
        for animal in animals {
            animal.speak();
        }
    }
    

    类似于C++虚表的原理,rust使用了胖指针实现子类型多态, 例子中将具体的Dog和Cat转为胖指针,每个胖指针都有2个字段,为固定大小。一个字段指向数据结构本身,一个字段指向函数表


    trait object

    3. 小结

    rust泛型包括参数泛型和特型泛型(1)数据结构泛型 2)函数泛型 3)trait泛型),也包括子类型泛型(trait object)。对比C++和Java,都是在类和函数中表现泛型,其原理是想通的,学习的时候要仔细琢磨一下。

    相关文章

      网友评论

          本文标题:rust泛型编程范式

          本文链接:https://www.haomeiwen.com/subject/bhvjcjtx.html