利用 trait 实现多态

news/2025/2/12 15:10:28/

我在书上看到基于 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://www.ppmy.cn/news/979808.html

相关文章

LeetCode74.Search-A-2d-Matrix<搜索二维矩阵>

题目&#xff1a; 思路&#xff1a; 矩阵&#xff0c;搜索数是否在矩阵内。那就查找他是否在每一行中。如果符合这一行的范围&#xff0c;那就一直找这一列是否存在&#xff0c;如果存在返回true&#xff1b;否则false&#xff1b; 代码是&#xff1a; //codeclass Solution …

Layui下拉多选框

标题xmSelect插件&#xff1a; xmSelect文档 下载Layui第三方插件 下拉多选框效果&#xff1a; 实现方法(例子)&#xff1a; 将xmSelect插件的xm-select.js文件引入到layui中&#xff1a; <script src"public/js/xm-select/xm-select.js"></script> …

13.5.4 【Linux】常用模块简介

登陆所需要的PAM流程&#xff1a; 上面这个表格当中使用到非常多的 PAM 模块&#xff0c;每个模块的功能都不太相同&#xff0c;详细的模块情报可以在你的系统中找到&#xff1a; /etc/pam.d/*&#xff1a;每个程序个别的 PAM 配置文件&#xff1b; /lib64/security/*&#x…

论文阅读与管理方法论

文章目录 为什么读论文论文类型综述论文专题论文 论文质量角度关于如何找论文的小Tips如何整理论文读论文的困境如何读论文不同人群阅读差异读论文三部曲&#xff1a;泛读、精读、总结泛读&#xff1a;快速浏览&#xff0c;把握概要。泛读目标及效果自测 精读&#xff1a;选出精…

什么是SVM算法?硬间隔和软间隔的分类问题

SVM全称是supported vector machine(支持向量机)&#xff0c;即寻找到一个超平面使样本分成两类&#xff0c;并且间隔最大。 SVM能够执行线性或⾮线性分类、回归&#xff0c;甚至是异常值检测任务。它是机器学习领域最受欢迎的模型之一。SVM特别适用于中小型复杂数据集的分类。…

蓝桥杯专题-真题版含答案-【生命之树】【消除尾一】【密码脱落】【生日蜡烛】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

React的hooks---useRef

useRef 用于返回一个可变的 ref 对象&#xff0c;其 .current 属性被初始化为传入的参数&#xff08;initialValue&#xff09; useRef 创建的 ref 对象就是一个普通的 JavaScript 对象&#xff0c;而 useRef() 和自建一个 {current: ...} 对象的唯一区别是&#xff0c;useRef…

Spring(二):更简单的存储与读取 Bean

通过上一章的Spring&#xff0c;我们基本实现了Spring 的读取与存储&#xff0c;但是在操作过程中&#xff0c;读取与存储并没有那么得“简单” 一套流程还是很复杂&#xff0c;所以&#xff0c;本章来介绍更加简单得读取与存储。 在 Spring 中想要更简单的存储和读取对象的核…