Rust的智能指针有哪些?大多数人都能马上答出Box、Rc和Arc、Ref和在异步编程中很常见的Pin等等。不过,有一个可能经常被大多数人遗忘的类型,它功能强大,利用好了可以节省很多复制开销;它就是这篇文章的主角:Cow<B>。
什么是COW(Copy-On-Write)?
在开始之前,有必要先介绍一下COW(Copy-On-Write,写时复制)的概念。COW是一种用于资源管理的优化策略,在操作系统中应用非常广泛。COW的核心思想是当多个任务需要读取同一个资源(比如内存中的数据、文件)的时候,它们会共享同一份资源副本,而不是为每个任务复制一份资源副本。只有当某个任务需要修改这个资源时,才会为这个任务创建一份资源副本。
需要注意的是,上述的整个过程对任务(也就是程序员编写的用户程序)来说都是不可见的;对程序员来说,他并不知道他所使用的资源在发生写操作时才被真正地复制了一份,自始至终他仿佛就像在独占整份资源一样。
COW在文件系统、虚拟内存管理中都有非常成熟的应用;在编程语言中,也被广泛应用于优化字符串、集合的处理。
Cow:定义
Rust的Cow<B>是一个枚举类型,包含两个成员:Borrowed和Owned。不过,我们几乎不会直接用到它的成员,因为Cow<B>实现了Deref特征,这使得我们可以通过Deref转换这一语法糖来便捷地直接使用Cow<B>中的内容。有关Deref转换可以阅读我之前的文章。- pub enum Cow<'a, B>
- where
- B: 'a + ToOwned + ?Sized,
- {
- Borrowed(&'a B),
- Owned(<B as ToOwned>::Owned),
- }
复制代码 这种方案可以吗?仔细想想,当字节切片中有UTF-8中不支持的错误字符时,错误字符需要被替换成“�”;直接返回&str的话是做不了对字符串内容的修改的。
返回String呢?
顺着刚才的思路,因为我们可能需要修改字符串,所以我们就需要返回&str的栈上类型String,合情合理:- use std::borrow::Cow;
- fn main() {
- let foo = "Hello World";
- let mut bar: Cow<str> = Cow::from(foo);
- println!("{bar}"); // 这里没有发生复制
-
- bar.to_mut().push_str(" Rust"); // 这里发生了复制
- println!("{bar}");
-
- println!("{foo}"); // 原来的字符串foo仍然可用,而且没有变化
- }
复制代码 不过,另一个问题冒出来了:虽然返回String完美地解决了修改字符串之后会导致新字符串无处存放的问题,但是如果旧的字符串(字节切片)不需要修改的话,也需要被复制到String中,这无形中增加了很多不必要的消耗;而且,字节切片中有错误字符是概率很小的事件,为了小概率事件影响拖累大概率发生的正常情况的性能,这值得吗?
这时,我一拍大腿:在需要修改时返回String,不需要修改时返回&str不就好了?
返回(Option, Option)(或者Either)
这样,上面所描述的性能和功能矛盾就解决了:- use std::borrow::Cow;
- fn main() {
- let str_ = "Hello World";
- let string = String::from("Hello World!");
-
- let foo: Cow<str> = Cow::Borrowed(str_);
- let bar: Cow<str> = Cow::Owned(string);
-
- // 这里string不再可用
- // println!("{string}");
- }
复制代码 使用了Cow之后,它不仅可以在需要修改字符串时克隆并返回新数据,更可以在绝大多数普通情况之下直接借用数据;更妙的是,它可以享受Deref转换的语法糖,可谓十分完美!
总结
Cow是Rust中非常有用的一个类型,虽然日常开发中几乎用不到它,但是某些性能敏感的场景下善用Cow说不定会有奇效喔~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |