Skip to content
On this page

智能指针4: exercises/smart_pointers/cow1.rs

题目

rust
// cow1.rs
//
// This exercise explores the Cow, or Clone-On-Write type. Cow is a
// clone-on-write smart pointer. It can enclose and provide immutable access to
// borrowed data, and clone the data lazily when mutation or ownership is
// required. The type is designed to work with general borrowed data via the
// Borrow trait.
//
// This exercise is meant to show you what to expect when passing data to Cow.
// Fix the unit tests by checking for Cow::Owned(_) and Cow::Borrowed(_) at the
// TODO markers.
//
// Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

use std::borrow::Cow;

fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> {
    for i in 0..input.len() {
        let v = input[i];
        if v < 0 {
            // Clones into a vector if not already owned.
            input.to_mut()[i] = -v;
        }
    }
    input
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn reference_mutation() -> Result<(), &'static str> {
        // Clone occurs because `input` needs to be mutated.
        let slice = [-1, 0, 1];
        let mut input = Cow::from(&slice[..]);
        match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()),
            _ => Err("Expected owned value"),
        }
    }

    #[test]
    fn reference_no_mutation() -> Result<(), &'static str> {
        // No clone occurs because `input` doesn't need to be mutated.
        let slice = [0, 1, 2];
        let mut input = Cow::from(&slice[..]);
        match abs_all(&mut input) {
            // TODO
        }
    }

    #[test]
    fn owned_no_mutation() -> Result<(), &'static str> {
        // We can also pass `slice` without `&` so Cow owns it directly. In this
        // case no mutation occurs and thus also no clone, but the result is
        // still owned because it was never borrowed or mutated.
        let slice = vec![0, 1, 2];
        let mut input = Cow::from(slice);
        match abs_all(&mut input) {
            // TODO
        }
    }

    #[test]
    fn owned_mutation() -> Result<(), &'static str> {
        // Of course this is also the case if a mutation does occur. In this
        // case the call to `to_mut()` returns a reference to the same data as
        // before.
        let slice = vec![-1, 0, 1];
        let mut input = Cow::from(slice);
        match abs_all(&mut input) {
            // TODO
        }
    }
}

题目解析

Cow是一个enum,包含一个对类型B的只读引用,或者包含一个拥有类型B的所有权的数据。使用Cow使我们在返回数据时提供了两种可能: 要么返回一个借用的数据(只读),要么返回一个拥有所有权的数据(可读写)。

into_owned方法用于抽取Cow所包裹类型的所有者权的数据,如果它还没有所有权数据将会克隆一份。

to_mut方法从Cow所包裹类型的所有者权的数据获得一个可变引用,如果它还没有所有权数据将会克隆一份再返回其可变引用。

调用to_mut()方法可以得到一个具有所有权的值的可变引用,注意在已经具有所有权的情况下,也可以调用to_mut但不会产生新的克隆,多次调用to_mut只会产生一次克隆。

调用into_owned()方法可以得到具有所有权的值,如果之前CowBorrowed借用状态,调用into_owned将会克隆,如果已经是Owned状态,将不会克隆。

第一个单元测试reference_mutation,因为向Cow::from传递的是借用,并且由于存在负数,所以要对其进行修改,也就是有写入,所以返回一个拥有所有权的数据。

第二个单元测试reference_no_mutation,向Cow::from传递的同样是借用,但是由于都是正数,所以在取绝对值的过程中不需要进行任何修改,所以应该是返回一个借用。

rust
#[test]
    fn reference_no_mutation() -> Result<(), &'static str> {
        // No clone occurs because `input` doesn't need to be mutated.
        let slice = [0, 1, 2];
        let mut input = Cow::from(&slice[..]);
        match abs_all(&mut input) {
            Cow::Borrowed(_) => Ok(()), 
            _ => Err("Expected owned value"), 
        }
    }

第三个单元测试owned_no_mutation,向Cow::from传递的本身就是有所有权的类型,也就是已经是Owned状态,不管期间做了什么,也不会发生克隆,所以返回还是Owned状态。

rust
#[test]
    fn owned_no_mutation() -> Result<(), &'static str> {
        // We can also pass `slice` without `&` so Cow owns it directly. In this
        // case no mutation occurs and thus also no clone, but the result is
        // still owned because it was never borrowed or mutated.
        let slice = vec![0, 1, 2];
        let mut input = Cow::from(slice);
        match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()), 
            _ => Err("Expected owned value"), 
        }
    }

第四个单元测试owned_mutation与第三个同理,不会发生克隆:

rust
#[test]
    fn owned_mutation() -> Result<(), &'static str> {
        // Of course this is also the case if a mutation does occur. In this
        // case the call to `to_mut()` returns a reference to the same data as
        // before.
        let slice = vec![-1, 0, 1];
        let mut input = Cow::from(slice);
        match abs_all(&mut input) {
            Cow::Owned(_) => Ok(()), 
            _ => Err("Expected owned value"), 
        }
    }

Powered by VitePress