Rust写时复制Cow<T>

逻辑映月使
• 阅读 2263

写时复制(Copy on Write)技术是一种程序中的优化策略,多应用于读多写少的场景。主要思想是创建对象的时候不立即进行复制,而是先引用(借用)原有对象进行大量的读操作,只有进行到少量的写操作的时候,才进行复制操作,将原有对象复制后再写入。这样的好处是在读多写少的场景下,减少了复制操作,提高了性能。

Rust中对应这种思想的是智能指针Cow<T>,定义如下:

pub enum Cow<'a, B> 
where
    B: 'a + ToOwned + 'a + ?Sized, 
 {
    Borrowed(&'a B),    //用于包裹引用
    Owned(<B as ToOwned>::Owned),   //用于包裹所有者
}

可以看到是一个枚举体,包括两个可选值,一个是“借用”,一个是“所有”。具体含义是:以不可变的方式访问借用内容,在需要可变借用或所有权的时候再克隆一份数据。

下面举个例子说明Cow<T>的应用:

use std::borrow::Cow;

fn abs_all(input: &mut Cow<[i32]>) {
    for i in 0..input.len() {
        let v = input[i];
        if v < 0 {
            input.to_mut()[i] = -v;
        }
    }

    println!("value: {:?}", input);
}

fn main() {
    // 只读,不写,没有发生复制操作
    let a = [0, 1, 2];
    let mut input = Cow::from(&a[..]);
    abs_all(&mut input);
    assert_eq!(input, Cow::Borrowed(a.as_ref()));

    // 写时复制, 在读到-1的时候发生复制
    let b = [0, -1, -2];
    let mut input = Cow::from(&b[..]);
    abs_all(&mut input);
    assert_eq!(input, Cow::Owned(vec![0,1,2]) as Cow<[i32]>);

    // 没有写时复制,因为已经拥有所有权
    let mut input = Cow::from(vec![0, -1, -2]);
    abs_all(&mut input);
    assert_eq!(input, Cow::Owned(vec![0,1,2]) as Cow<[i32]>);
    
    let v = input.into_owned();
    assert_eq!(v, [0, 1, 2]);
}

上面这个用例已经讲明了Cow<T>的使用,下面我们继续探索一下Cow<T>的实现细节。重点关注to_mutinto_owned的实现。

  • to_mut :就是返回数据的可变引用,如果没有数据的所有权,则复制拥有后再返回可变引用;
  • into_owned :获取一个拥有所有权的对象(区别与引用),如果当前是借用,则发生复制,创建新的所有权对象,如果已拥有所有权,则转移至新对象。
impl<B: ?Sized + ToOwned> Cow<'_, B> {
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn to_mut(&mut self) -> &mut <B as ToOwned>::Owned {
        // 如果时借用,则进行复制;如果已拥有所有权,则无需进行复制
        match *self {
            Borrowed(borrowed) => {
                *self = Owned(borrowed.to_owned());
                match *self {
                    Borrowed(..) => unreachable!(),
                    Owned(ref mut owned) => owned,
                }
            }   
            Owned(ref mut owned) => owned,  //这里解释了上个例子中,已拥有所有权的情况,无需再复制
        }
    }
    
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn into_owned(self) -> <B as ToOwned>::Owned {
        // 如果当前是借用,则发生复制,创建新的所有权对象,如果已拥有所有权,则转移至新对象。
        match self {
            Borrowed(borrowed) => borrowed.to_owned(),
            Owned(owned) => owned,
        }
    }
}
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
Johnny21 Johnny21
4年前
MySQL 8 复制(二)——半同步复制
目录一、简介直到目前的最新版本为止,MySQL缺省依然使用异步复制策略。简单说所谓异步复制,指的是主库写二进制日志、从库的I/O线程读主库的二进制日志写本地中继日志、从库的SQL线程重放中继日志,这三步操作都是异步进行的。如此选择的主要理由是出于性能考虑,与同步复制相比,异步复制显然更快,同时能承载更高的吞吐量。但异
Wesley13 Wesley13
3年前
Java深拷贝和浅拷贝
1.浅复制与深复制概念⑴浅拷贝(浅克隆)   复制出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。⑵深拷贝(深克隆)   复制出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,而不再是原有的那些被引用的对象。换言之,深复制
Wesley13 Wesley13
3年前
Java CopyOnWrite容器
   CopyOnWrite简称COW(写时复制),是一种程序设计中的优化策略,读取时,直接读取,写入时,copy一个副本,在这个副本上进行写入,写入完成,用副本替换原数据,这是一种延时懒惰策略。   从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,CopyOnWriteArrayList和CopyOnW
Wesley13 Wesley13
3年前
Java8 容器类详解
 ArrayListVectorCopyOnWriteArrayListLinkedListHashMapConcurrentHashMapLinkedHashMapLinkedBlockingQueuePriorityQueue使用场景随机访问ArrayList的线程安全版读多写少,写加锁,写操作在复制的
Wesley13 Wesley13
3年前
JAVA中 ReentrantReadWriteLock读写锁详系教程,包会
一、读写锁简介现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。 针对这种场景,JAVA的并发包提供了读写锁ReentrantReadW
Wesley13 Wesley13
3年前
Java中的读写锁ReadWriteLock
ReadWriteLock是JDK中的读写锁接口ReentrantReadWriteLock是ReadWriteLock的一种实现读写锁非常适合读多写少的场景。读写锁与互斥锁的一个重要区别是读写锁允许多个线程同时读共享变量,这是读写锁在读多写少的情况下性能较高的原因。读写锁的原则:多个线程可同时读共享变量只允许一
Easter79 Easter79
3年前
String封装——读时共享,写时复制
String封装——读时共享,写时复制本文由乌合之众瞎写http://my.oschina.net/oloroso(http://my.oschina.net/oloroso)碰到过一位一直怀疑C标准库(STL)效率的人,他说STL效率太低,企业开发根本不会用。我是持反对意见的。说这话的人,肯定没有做过大量的调查。没有调查就没有发言权
Stella981 Stella981
3年前
ReentrantReadWriteLock读写锁详解
一、读写锁简介现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。 针对这种场景,JAVA的并发包提供了读写锁ReentrantReadW
Stella981 Stella981
3年前
CopyOnWrite容器
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWri
Wesley13 Wesley13
3年前
PHP内核探索:写时复制COW机制
写时复制(CopyonWrite,也缩写为COW),顾名思义,就是在写入时才真正复制一份内存进行修改。COW最早应用在\nix系统中对线程与内存使用的优化,后面广泛的被使用在各种编程语言中,如C的STL等。在PHP内核中,COW也是主要的内存优化手段。在前面关于变量和内存的讨论中,引用计数对变量的销毁与回收中起着至关重要的标识作用。引用计数