Rust特征对象

一、特征对象是什么,有什么用,怎么用

1、特征对象是什么

特征对象指向实现了 某种特征的类型的实例,这种映射关系是存储在一张表中,可以在运行时通过特征对象找到具体调用的类型方法

可以通过 & 引用 或者 Box 智能指针 的方式来创建特征对象

这个说法太抽象了,我们来看一点例子

2、特征对象有什么用

在Rust特征那一篇文章的最后有一段代码提到

rust 复制代码
pub trait Summary {
  fn summarize(&self) -> String;
}
pub struct Post {
  pub title: String, // 标题
  pub author: String, // 作者
  pub content: String, // 内容
}

impl Summary for Post {
  fn summarize(&self) -> String {
      format!("文章{}, 作者是{}", self.title, self.author)
  }
}

pub struct Weibo {
  pub username: String,
  pub content: String
}

impl Summary for Weibo {
  fn summarize(&self) -> String {
      format!("{}发表了微博{}", self.username, self.content)
  }
}
fn returns_summarizable(switch: bool) -> impl Summary {
  if switch {
      Post {
          title: String::from(
              "Penguins win the Stanley Cup Championship!",
          ),
          author: String::from("Iceburgh"),
          content: String::from(
              "The Pittsburgh Penguins once again are the best \
               hockey team in the NHL.",
          ),
      }
  } else {
      Weibo {
          username: String::from("horse_ebooks"),
          content: String::from(
              "of course, as you probably already know, people",
          ),
      }
  }
}
fn main() {
 
  _a =returns_summarizable(true);
}

当我们想通过impy trait来企图达到返回多态类型的目的,发现并不允许这样做,会报错。

报错提示我们 if 和 else 返回了不同的类型。要返回相同的类型

那我们要怎么做?这时候就涉及到特征对象了

3、特征对象怎么用

我们可以通过&和Box智能指针去包住特征对象

特征对象的语法是:dyn+特征类型名称

dyn 关键字只用在特征对象的类型声明上,在创建时无需使用 dyn

那我们可以看看前面的代码怎么修改

(1)Box智能指针类型的代码

rust 复制代码
pub trait Summary {
    fn summarize(&self) -> String;
}
pub struct Post {
    pub title: String,   // 标题
    pub author: String,  // 作者
    pub content: String, // 内容
}

impl Summary for Post {
    fn summarize(&self) -> String {
        format!("文章{}, 作者是{}", self.title, self.author)
    }
}

pub struct Weibo {
    pub username: String,
    pub content: String,
}

impl Summary for Weibo {
    fn summarize(&self) -> String {
        format!("{}发表了微博{}", self.username, self.content)
    }
}
fn returns_summarizable(switch: bool) -> Box<dyn Summary> {
    if switch {
        Box::new(Post {
            title: String::from("Penguins win the Stanley Cup Championship!"),
            author: String::from("Iceburgh"),
            content: String::from(
                "The Pittsburgh Penguins once again are the best \
               hockey team in the NHL.",
            ),
        })
    } else {
        Box::new(Weibo {
            username: String::from("horse_ebooks"),
            content: String::from("of course, as you probably already know, people"),
        })
    }
}
fn main() {
    let _a = returns_summarizable(true);
}

可以看到,返回参数类型说明和返回的对象实例的方式都不同

返回参数 类型是 Box<dyn Summar (这里的写法看上面的代码吧,我不知道怎么打转义)

返回的实例 对象是使用Box::new的形式构建的

(2)&构建特征对象

这个涉及到生命周期问题哈哈改不对,后面再改进这方面,如果有人想知道可以评论区艾特我记得更新哈哈

4、特征对象的动态分发

动态分发(dynamic dispatch),直到运行时,才能确定需要调用什么方法。之前代码中的关键字 dyn 正是在强调这一"动态"的特点。

但是在返回参数中,他需要唯一确定返回值的大小,而之前imply trait来作为返回参数,当返回不同类型时,大小就不唯一了,所以就报错了。

那么Box<dyn trait是怎么做的呢

Box<dyn trait这个类型的实例大小是固定的。

ptr是一个引用,这个引用的大小是固定的,指向了底层T实例的数据,T是可变的

vptr 指向一个虚表 vtable,vtable 中保存了类型 Button 或类型 SelectBox 的实例对于可以调用的实现于特征 Draw 的方法。当调用方法时,直接从 vtable 中找到方法并调用。

之所以要使用一个 vtable 来保存各实例的方法,是因为实现了特征 Draw 的类型有多种 ,这些类型拥有的方法各不相同 ,当将这些类型的实例都当作特征 Draw 来使用 时(此时,它们全都看作是特征 Draw 类型的实例),有必要区分这些实例各自有哪些方法可调用

但是我们用Box<dyn trait传递实例 的时候,这个实例仅仅拥有该特征的方法,其它方法(包括原来自己的)无法调用,而实例数据是可以访问的。严格意义上来说,这个Box<dyn trait是特征对象的实例。

5、特征对象的限制

不是所有特征都能拥有特征对象,只有对象安全的特征才行。当一个特征的所有 方法都有如下属性时,它的对象才是安全的:

  1. 方法的返回类型不能是 Self
  2. 方法没有任何泛型参数

6、Self 和self

在 Rust 中,有两个self,一个指代当前的实例对象,一个指代特征或者方法类型的别名:

rust 复制代码
trait Draw {
    fn draw(&self) -> Self;
}

#[derive(Clone)]
struct Button;
impl Draw for Button {
    fn draw(&self) -> Self {
        return self.clone()
    }
}

fn main() {
    let button = Button;
    let newb = button.draw();
}

上述代码中,self指代的就是当前的实例对象,也就是 button.draw() 中的 button 实例,Self 则指代的是 Button 类型。

相关推荐
Swift社区6 分钟前
Swift 解 LeetCode 321:拼接两个数组中的最大数,贪心 + 合并全解析
开发语言·leetcode·swift
ruan11451432 分钟前
Java Lambda 类型推断详解:filter() 方法与 Predicate<? super T>
java·开发语言·spring·stream
广东数字化转型35 分钟前
java jar 启动应用程序
开发语言·python
阿幸软件杂货间37 分钟前
阿幸课堂随机点名
android·开发语言·javascript
threelab1 小时前
three案例 Three.js波纹效果演示
开发语言·javascript·ecmascript
黎明smaly1 小时前
【排序】插入排序
c语言·开发语言·数据结构·c++·算法·排序算法
辣辣y1 小时前
python基础day08
开发语言·python
Json____2 小时前
使用python的 FastApi框架开发图书管理系统-前后端分离项目分享
开发语言·python·fastapi·图书管理系统·图书·项目练习
人生在勤,不索何获-白大侠2 小时前
day16——Java集合进阶(Collection、List、Set)
java·开发语言
LIN-JUN-WEI3 小时前
[ESP32]VSCODE+ESP-IDF环境搭建及blink例程尝试(win10 win11均配置成功)
c语言·开发语言·ide·vscode·单片机·学习·编辑器