利用 trait 实现多态

news/2024/7/7 22:35:26

我在书上看到基于 std::io::Write 的示例,它是一个 trait 类型,内部声明了一些方法。和 go 语言不同,rust 中类型必须明确实现 trait 类型,而 go 语言属于 duck 模式。

std::io::Write

下面的例子中调用 write_all 方式来演示,write_all 也是 Write trait 声明要实现的方法之一。
在这里插入图片描述
示例中 buf 的类型是 Vec<u8> 类型,调用 write_all 向其中写入数据,然后将 buf 转换为 &str 类型打印输出。

但下面的代码会编译报错,提示 method not found,buf 实现了 Write trait,但编译器找不到这个 write_all 方法。问题的原因:特型本身必须在作用域中,否则,特型的所有方法都是隐藏的。我理解,这属于 rust 类型和 trait 的自动绑定过程。

解决编译问题的方法非常简单,只需要在文件头部导入 use std::io::Write; 就可以了。

use std::str;
// use std::io::Write;

fn main() {
    let mut buf: Vec<u8> = vec![];
    let _ = buf.write_all(b"hello");

    let s = match str::from_utf8(buf.as_slice()) {
        Ok(v) => v,
        Err(e) => panic!("Invalid UTF-8 sequence:{}", e),
    };

    println!("{}", s);
}

下面我们看下 write_all 方法提供的 example,创建一个文件,然后向其中写入内容。buffer 对象调用了结构体声明的 write_all 方法。这里就明显区别于 Vec 类型,File 调用的过程没有在头部声明 trait 也执行成功了。

use std::io::prelude::*;
use std::fs::File;

fn main() -> std::io::Result<()> {
    let mut buffer = File::create("foo.txt")?;

    buffer.write_all(b"some bytes")?;
    Ok(())
}

我们使用 trait 主要是为了设计通用性考虑,会将类型声明为 trait 类型。因为 std::fs::File 实现了 trait 特性,我们尝试将 buffer 对象声明 trait 类型。如下代码所示,遗憾的是,rust 不允许声明 Write 的变量类型。理由是:变量大小必须在编译时就确定,而实现 Write 的类型可以是任意大小。

下面的代码编译无法通过:

use std::io::prelude::*;
use std::fs::File;
use std::io::Write;

fn main() -> std::io::Result<()> {
    let mut buffer: Write = File::create("foo.txt")?;

    buffer.write_all(b"some bytes")?;
    Ok(())
}

要想声明 Write trait 类型,必须使用引用,因为指针的大小是固定的。Go 语言的接口类型可以接收任意类型的值,也是通过指针来做的类型大小屏蔽。

内部实现上,&Write 和 interface 的实现就非常接近,都是内部包含两个指针,分别指向实际的值和对应的类型。实际的值比较好理解,关键是对应的类型。

use std::fs::File;
use std::io::Write;

fn main() -> std::io::Result<()> {
    let mut foo = File::create("foo.txt")?;
    let buffer: &mut dyn Write = &mut foo;

    buffer.write_all(b"some bytes")?;
    Ok(())
}

http://lihuaxi.xjx100.cn/news/1375188.html

相关文章

01 矩阵(力扣)多源广度优先搜索 JAVA

给定一个由 0 和 1 组成的矩阵 mat &#xff0c;请输出一个大小相同的矩阵&#xff0c;其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 输入&#xff1a;mat [[0,0,0],[0,1,0],[0,0,0]] 输出&#xff1a;[[0,0,0],[0,1,0],[0,0,0]] 输入…

2023百强县名单出炉!千亿县达54个

作为国民经济发展中的基本单元&#xff0c;县域经济发挥着重要作用。 赛迪顾问25日发布的《2023中国县域经济百强研究》&#xff08;下称“报告”&#xff09;显示&#xff0c;千亿县达54个&#xff0c;百强县前10名中江苏省独占6席。 在百强县前10名中&#xff0c;江苏省占席最…

如何在3ds max中创建可用于真人场景的巨型机器人:第 1部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建主体 步骤 1 打开 3ds Max。 打开 3ds Max 步骤 2 在左侧视口中&#xff0c;按键盘上的 Alt-B 键。它 打开视口配置窗口。 打开“锁定缩放/平移”和“匹配位图”选项。单击“文件”并转到参考 …

Python数据分析实战-利用limit 与 offset进行数据库数据批量查询与处理(附源码和实现效果)

实现功能 利用limit 与 offset进行数据库数据批量查询与处理 实现代码 def query_batch(self,engine,batch_step,end,sql):session make_session(engine)cursor session.execute(sql.format(batch_step, end))fields cursor._metadata.keysdf pd.DataFrame([dict(zip(fi…

【Kafka】消息队列Kafka基础

目录 消息队列简介消息队列的应用场景异步处理系统解耦流量削峰日志处理 消息队列的两种模式点对点模式发布订阅模式 Kafka简介及应用场景Kafka比较其他MQ的优势Kafka目录结构搭建Kafka集群编写Kafka一键启动/关闭脚本 Kafka基础操作创建topic生产消息到Kafka从Kafka消费消息使…

Redis主从模式搭建

Redis主从模式搭建 一、下载redis二、修改配置文件1.复制一份redis.conf2.修改配置 三、开启服务1.启动服务2.如果没有src中没有redis-server2.打开服务 四、设置主从五、检验主从特性 一、下载redis cd /opt wget http://download.redis.io/releases/redis-7.0.5.tar.gz tar …

如何写好测试报告?

目录 一、目标 二、模板的使用 三、修订记录 四、内容应该清晰易懂&#xff0c;简明扼要 五、绝不放过一个错字 六、遗留问题单 七、产出成果恰当呈现 一、目标 本文介绍测试人员编写软件测试报告常见的疏漏&#xff0c;以便大家避免&#xff0c;更好让测试成果呈现给客…

数据结构【数组、串、广义表】

第四章 数组、串、广义表 一、数组 1.概念&#xff1a;线性表是通过数组实现的&#xff0c;数组是线性表的推广&#xff0c;数组只有存取元素和修改元素的操作&#xff08;除了初始化和销毁&#xff09;&#xff1b; 2.数组的存储结构&#xff1a;一个数组的所有元素在内存中占…