Rust常用特型之AsRef和AsMut特型

2024-04-21 07:36
文章标签 rust 常用 asref 特型 asmut

本文主要是介绍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用于具体函数,这两者的场景并不相同。

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

这篇关于Rust常用特型之AsRef和AsMut特型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/922511

相关文章

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re

在Rust中要用Struct和Enum组织数据的原因解析

《在Rust中要用Struct和Enum组织数据的原因解析》在Rust中,Struct和Enum是组织数据的核心工具,Struct用于将相关字段封装为单一实体,便于管理和扩展,Enum用于明确定义所有... 目录为什么在Rust中要用Struct和Enum组织数据?一、使用struct组织数据:将相关字段绑

Python中操作Redis的常用方法小结

《Python中操作Redis的常用方法小结》这篇文章主要为大家详细介绍了Python中操作Redis的常用方法,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解一下... 目录安装Redis开启、关闭Redisredis数据结构redis-cli操作安装redis-py数据库连接和释放增

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

一文详解Python中数据清洗与处理的常用方法

《一文详解Python中数据清洗与处理的常用方法》在数据处理与分析过程中,缺失值、重复值、异常值等问题是常见的挑战,本文总结了多种数据清洗与处理方法,文中的示例代码简洁易懂,有需要的小伙伴可以参考下... 目录缺失值处理重复值处理异常值处理数据类型转换文本清洗数据分组统计数据分箱数据标准化在数据处理与分析过

Java中Object类的常用方法小结

《Java中Object类的常用方法小结》JavaObject类是所有类的父类,位于java.lang包中,本文为大家整理了一些Object类的常用方法,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. public boolean equals(Object obj)2. public int ha

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情