Appearance
智能指针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()
方法可以得到具有所有权的值,如果之前Cow
是Borrowed
借用状态,调用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"),
}
}