Rust常用特型之AsRef和AsMut特型

在Rust标准库中,存在很多常用的工具类特型,它们能帮助我们写出更具有Rust风格的代码。

今天我们要学习的AsRefAsMut,和前面学习的Deref和DerefMut有那么一点混淆的地方。

当一个类型U实现了AsRef<T>,那么我们可以高效的从U借出一个T的引用。AsMut对应的是借出可变引用。它们的定义如下:

trait AsRef<T: ?Sized> {fn as_ref(&self) -> &T;
}
trait AsMut<T: ?Sized> {fn as_mut(&mut self) -> &mut T;
}

例如,Vec<T>实现了AsRef<T>String实现了AsRef<str>AsRef<[u8]>

AsRef应用的主要场景在于实现函数参数可接收类型的灵活性。例如,std::fs::File::open函数定义类似如下:

fn open<P: AsRef<Path>>(path: P) -> Result<File>

这里open函数真正想接收的是&Path,它代表一个文件系统路径。但是根本函数签名,它可以接受任何能借出一个&Path的值,也就是任何实现了AsRef<Path>的类型都可以。这些类型包含Stringstr,还有操作系统接口字符串类型OsStringOsStr以及PathBufPath本身等。这就是为什么你可以向open函数直接传递字符串字面值(&‘static str’)。

所有标准库的文件系统访问函数都采用这个方式来接收path参数,对调用者来讲,和C++中的函数重载比较类似,虽然Rust采用了一种不同的方式。

但是这里有一个细节,我们传递的参数是&str,然而却是str实现了AsRef<Path>,并没有前面的&。我们在前面的DerefDerefMut中讲过,涉及到泛型函数类型参数约束的时候,强制解引用是不发生作用的,所以它在这里是没有帮助的。

幸运的是,标准库增加了一个空实现

impl<'a, T, U> AsRef<U> for &'a Twhere T: AsRef<U>,T: ?Sized, U: ?Sized
{fn as_ref(&self) -> &U {(*self).as_ref()}
}

上面的代码的意思就是,如果T实现了AsRef<U>,那么&T也实现了AsRef<U>。这里 函数as_ref中的self其实是&&T类型,对其取*,得到 &T, 然后再调用.as_ref(),正好符合AsRef的定义,返回一个&U。所以和Deref类似,这里我们看到&可以直接忽略。

由于str实现了AsRef<Path>,那么&str也实现了。使用这种方式,我们可以在检查类型变量AsRef条件约定时得到一个有限功能的强制解引用。

你可能觉得如果一个类型实现了AsRef<T>,那么它也应该实现了AsMut<T>。然而,有一些场景并不符合这种假定。比如,我们注意到了String实现了了AsRef<[u8]>,这是没有问题的,因为每个字符串都包含了一个字节缓冲用来访问二进制数据。但是,String进一步保证了这些字节都是正确编码的UTF-8文本。如果字符串实现了AsMut<[u8]>,那么使用者有就可能将字节缓冲区的内容改成任意内容,有可能是不正确的UTF-8编码,它违背了String的定义。只有修改T时并不违反类型T的约定时才能在另一个类型上实现AsMut<T>

虽然AsRefAsMut很简单,但是它们却提供了一个标准的方法来实现引用转换,从而避免用户编写越来越多的特定转换特型。在可以使用AsRef<Foo>的时候用户需要尽量避免使用自定义的AsFoo特型来转换引用。

这里的意思是如果不使用AsfRef特型,对于Rust开发者来说,常见的做法就是自己定义一个转换特型,例如AsFoo。这样每个类型来一次,用户自定义的特型就会越来越多(增殖),所以我们可以使用标准库的AsRef<Foo>来简化这种设计,不用定义一大堆自己新建的特型。

这里AsRef的应用我们举一个简单的例子,就以上面的open函数来讲,open函数就是产品,调用者就是用户,用户需要输入&Path才能使用,但是这对用户限制太大了,因为用户很少有Path,怎么办呢?

用户至上嘛,用户动不了,只能动产品,于是定义一个协议,用于将其它类型转换成&Path,这个协议就是AsRef特型。

光有这个协议还是不够的,用户不会自己根据这个协议写一个转换,甚至用户都不知道这个协议,怎么办呢?就是平台自己为用户常见输入类型来实现这个协议 ,这也就标准库自动为strString实现AsRef<Path>的原因。

这下大功告成,用户只需要输入字符串就行了,并不需要了解Path是什么。通过函数类型参数约定,可以检查用户调用是否正确,然后在函数体内部直接调用 p.as_ref()就得到了我们想要的&Path了。这样所有的实现对用户来说是隐藏的,用户只需要输入String就行了。

Rust设计还是很巧妙的,比如最底层的数据类型是str而不是&str,常用特型都是在str上实现,然而通过标准库的一些额外实现,我们可以直接在前面加多个&&并且无障碍使用,给用户的感觉就是底层数据类型就是&str,这和用户平常的感觉是一致的。

最后,我们来讲一下AsRefDeref的混淆点。他们俩都可以用来转换引用并实现函数接收参数的灵活性,但不同的是:

  • Deref主要用于智能指针和自定义解引用操作(.*操作符),AsRef不涉及到这些

  • 用于函数重载(多态时),AsRef用于泛型函数,而Deref用于具体函数,这两者的场景并不相同。

  • 在检查条件约束时,强制解引用是不起作用的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/1210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

K8S哲学 - Pod、RC、RS、deployment

pod&#xff08;最小的可部署单元&#xff09; 容器组&#xff08;运行一个或多个容器&#xff09; Pod(容器组&#xff09;是Kubernetes 中最小的可部署单元。 一个Pod(容器组&#xff09;包含了一个应用程序容器&#xff08;某些情况下是多个容器&#xff09;、存储资源、 一…

C++练级之路——类和对象(中二)

1、运算符重载 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载是具有特殊函数名的函数&#xff0c;也是具有其返回值类型&#xff0c;函数名字以及参数列表&#xff0c;其返回值类型和参数列表与普通的函数类似。 函数名字为&#xff1a;关键字operator后面接需…

【C++初识继承】

博主首页&#xff1a; 有趣的中国人 专栏首页&#xff1a; C进阶 本篇文章主要讲解 继承 的相关内容 目录 1. 继承的概念和定义 1.1 继承的概念 1.2 继承的定义 1.2.1 继承定义格式 1.2.2 继承方式与访问修饰限定符 2. 基类和派生类对象赋值转换 3. 继承中的作用域 …

npm常用命令详解(二)

npm&#xff08;Node Package Manager&#xff09;是Node.js的包管理器&#xff0c;它允许你安装、共享和管理Node.js代码库。以下是一些常用的npm命令及其详解&#xff0c;以帮助您更好地理解和使用它。 1. npm doc 功能&#xff1a;查看指定包的文档。 语法&#xff1a;np…

linux离线安装mysql

一、下载mysql 地址&#xff1a;MySQL 这里选择64为还是32为要根据操作系统来 uname -m 二、上传解压配置mysql 使用root账户登录linux服务器&#xff0c;在opt文件下创建mysql文件夹 cd /opt sudo mkdir mysql 使用Xftp上传mysql压缩包到此文件夹下(自行决定路径) cd mysql/…

基于Kubernetes集群构建MongoDB

基于Kubernetes集群构建MongoDB 作者:行癫(盗版必究) 一:基础环境 1.Kubernetes集群正常运行 2.Harbor私有仓库正常运行 二:MongoDB项目部署 ​ MongoDB项目对应Kubernetes的yaml文件: --- apiVersion: v1 kind: Namespace metadata:name: m

力扣:104. 二叉树的最大深度(Java,DFS,BFS)

目录 题目描述&#xff1a;输入&#xff1a;输出&#xff1a;代码实现&#xff1a;1.深度优先搜索&#xff08;递归&#xff09;2.广度优先搜索&#xff08;队列&#xff09; 题目描述&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从…

【QT进阶】Qt http编程之后端API测试工具postman使用介绍

往期回顾 【QT进阶】Qt Web混合编程之使用ECharts显示各类折线图等-CSDN博客 【QT进阶】Qt Web混合编程之实现ECharts数据交互动态修改-CSDN博客 【QT进阶】Qt http编程之http与https简单介绍-CSDN博客 【QT进阶】Qt http编程之后端API测试工具postman使用介绍 其实这个工具的…

nextjs getStaticProps 和 getServerSideProps 区别

getStaticProps 和 getServerSideProps 是 Next.js 中用于数据获取的两个重要函数&#xff0c;它们在页面渲染时分别起到不同的作用。 1、getStaticProps&#xff1a; 用途&#xff1a;主要用于静态生成页面&#xff0c;即在构建时获取数据并将其预先注入到页面中。执行环境&…

Pytorch的下载安装

本文为自己整理的Pytorch下载相关的内容笔记&#xff0c;以便日后查阅 一. 基本命令 1.查看conda版本 conda --version2.创建conda新环境 conda create –n 名称 python版本3.查看已经创建的conda环境 conda info --envs4.进入虚拟环境 conda activate 环境名称 为了避免…

【Day 3】Ajax + Vue 项目、路由 + Nginx

1 Ajax Asynchronous JavaScript And XML 异步的 JavaScript 和 XML 作用&#xff1a; 数据交换 通过 Ajax 可以给服务器发送请求&#xff0c;并获取服务器响应的数据 异步交互 可以在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页的技术&#xf…

【大模型应用极简开发入门(1)】LLM概述:LLM在AI中所处位置、NLP技术的演变、Transformer与GPT、以及GPT模型文本生成逻辑

文章目录 一. AI中大语言模型的位置与技术发展1. 从AI到Transformer2. NLP&#xff1a;自然语言处理3. LLM大型语言模型&#xff1a;NLP的一种特定技术3.1. LLM定义3.2. LLM的技术发展3.2.1. n-gram模型3.2.2. RNN与LSTM 二. Transformer在LLM中脱颖而出1. Transformer架构能力…

编译一个基于debian/ubuntu,centos,arhlinux第三方系统

目录 前言 准备工作 下载linux源码进行编译 linux源码下载 网站 问题 解决办法 编译 可能会遇到的问题 chroot下载debian环境 进入虚拟环境 把chroot的根目录文件打包为.gz文件 编译init文件&#xff08;用于系统启动时的一系列引导&#xff09; 给予文件夹权限 …

碎碎笔记01

凹凸性 一元函数 凸函数&#xff1a;二阶导数>0 f ( x ) x 2 f(x) x^2 f(x)x2的二阶导数是 2&#xff0c;>0凹函数&#xff1a;二阶导数<0 驻点&#xff0c;拐点 驻点&#xff1a;增减性的交替点 拐点&#xff1a;凹凸性的交替点 脑补 f ( x ) s i n x f(x) …

【树莓派学习】hello,world!

系统安装及环境配置详见【树莓派学习】系统烧录及VNC连接、文件传输-CSDN博客 树莓派内置python3&#xff0c;可以直接利用python输出。

Docker 部署 MongoDB 数据库

文章目录 官网地址docker 网络mongod.conf部署 MongoDB部署 mongo-expressdocker-compose.ymlMongoDB shell 官网地址 https://www.mongodb.com/zh-cn docker 网络 # 创建 mongo_network 网络 docker network create mongo_network # 查看网络 docker network list # 容器连…

Don‘t fly solo! 量化之路,AI伴飞

在投资界&#xff0c;巴菲特与查理.芒格的神仙友谊&#xff0c;是他们财富神话之外的另一段传奇。巴菲特曾这样评价芒格&#xff1a;他用思想的力量拓展了我的视野&#xff0c;让我以火箭的速度&#xff0c;从猩猩进化到人类。 人生何幸能得到一知己。如果没有这样的机缘&…

【C++初阶】List使用特性及其模拟实现

1. list的介绍及使用 1.1 list的介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前…

学习在Debian系统上安装Shadowsocks教程

学习在Debian系统上安装Shadowsocks教程 安装shadowsocks-libev及其所需的依赖启动Shadowsocks服务&#xff1a;如果你想要通过代理本地流量&#xff0c;你可以使用ss-local&#xff1a;启动并设置ss-local&#xff1a;查看状态本地连接 安装shadowsocks-libev及其所需的依赖 …

如何创建响应式HTML电子邮件模板

在这个适合初学者的指南中&#xff0c;你将学习如何创建一个响应式电子邮件模板。你将跟随逐步说明以及代码片段设计一个在任何设备上都看起来很棒的电子邮件模板。 这个项目非常适合渴望掌握电子邮件设计基础的新手&#xff01; &#xff08;本文视频讲解&#xff1a;java56…