美文网首页
Rust学习——trait对象

Rust学习——trait对象

作者: P2501 | 来源:发表于2020-12-22 14:49 被阅读0次

    一、静态分发:依靠泛型支持,实际上为通过编译期将泛型类型扩展为实际类型,实现单态,最后的结果是代码量的膨胀。

    trait Foo {
        fn method(&self) -> String;
    }
    
    impl Foo for u8 {
        fn method(&self) -> String { format!("u8: {}", *self) }
    }
    
    impl Foo for String {
        fn method(&self) -> String { format!("string: {}", *self) }
    }
    
    fn do_something<T: Foo>(x: T) {
        x.method();
    }
    
    fn main() {
        let x = 5u8;
        let y = "Hello".to_string();
    
        do_something(x);
        do_something(y);
    }
    //-----------------------------------------------------------------------------------------
    // 在编译期,编译期会将泛型翻译为实际类型,针对具体类型分别实现函数,调用的时候调用的是各自的函数,如下
    fn do_something_u8(x: u8) {
        x.method();
    }
    
    fn do_something_string(x: String) {
        x.method();
    }
    
    fn main() {
        let x = 5u8;
        let y = "Hello".to_string();
    
        do_something_u8(x);
        do_something_string(y);
    }
    

    二、动态分发:利用对象指针和函数指针的组合实现运行期的多类型分发
    思路:

    1. 关键在于,当将trait impl到 对象之后,对象中重写的trait方法,会在虚函数表中存储该方法,
    2. 当通过(&对象 as &trait)后,便实现了指针连接,即将traitObject(胖指针)的一个指针指向对象,再将另一个指针指向一个虚函数表,
    3. 其中虚函数表中含有,在impl trait for 对象 时重写的方法地址和虚表的偏移量,将虚表地址和偏移量结合可以确认该对象所从写的trait方法
    4. 当调用该方法时,通过这个指针获取实际对象以及该实际对象重写的trait方法。
    // 定义trait及方法
    trait Bird {
        fn fly(&self);
    }
    
    struct Duck;
    struct Swan;
    
    // 将trait impl到 Duck中,将重写的trait方法存入虚函数表
    impl Bird for Duck {
        fn fly(&self){
            println!("duck duck");
        }
    }
    
    // 将trait impl到 Swan中,将重写的trait方法存入虚函数表
    impl Bird for Swan {
        fn fly(&self){
            println!("Swan Swan");
        }
    }
    
    // 定义一个调用函数
    // fn print_trait_obj(p: &dyn Bird){
    //     p.fly();
    // }
    
    fn main() {
        // 新建对象
        let duck = Duck;
    
        // 创建 对象的引用
        let p_duck = &duck;
    
        // 将对象引用 转换成 trait对象,这个过程中——trait对象为胖指针(指针1.p_duck;指针2.(虚函数表+Duck的trait方法在虚函数表中的偏移量))
        let p_bird = p_duck as &dyn Bird;
    
        // 当调用trait方法时,从指针1中获取对象,从指针2中获取trait方法
        // print_trait_obj(p_bird);
        p_bird.fly();  // 因为fly(&self), 所以等价于 (p_bird.vtable.fly)(p_duck)
    
        // 同理
        let swan = Swan;
        let p_swan = &swan;
        let p_bird = p_swan as &dyn Bird;  // 指针p_bird发生了重绑定
        p_bird.fly();
    
        // y 为struct
        // let y = TraitObject {
                //data存储实际值的引用
        //     data: &x,
                // vtable存储实际类型实现Foo的方法
        //     vtable: &Foo_for_u8_vtable
        // };
    }```

    相关文章

      网友评论

          本文标题:Rust学习——trait对象

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