Cow

Cow是一个非常方便的枚举。它的意思是 "写时克隆",如果你不需要String,可以返回一个&str,如果你需要,可以返回一个String。(它也可以对数组与Vec等做同样的处理)。

为了理解它,我们看一下签名。它说

pub enum Cow<'a, B>
where
    B: 'a + ToOwned + ?Sized,
 {
    Borrowed(&'a B),
    Owned(<B as ToOwned>::Owned),
}

fn main() {}

你马上就知道,'a意味着它可以和引用一起工作。ToOwned的特性意味着它是一个可以变成拥有类型的类型。例如,str通常是一个引用(&str),你可以把它变成一个拥有的String

接下来是?Sized。这意味着 "也许是Sized,但也许不是"。Rust中几乎每个类型都是Sized的,但像str这样的类型却不是。这就是为什么我们需要一个 & 来代替 str,因为编译器不知道大小。所以,如果你想要一个可以使用 str 这样的trait,你可以添加 ?Sized.

接下来是enum的变种。它们是 BorrowedOwned

想象一下,你有一个返回 Cow<'static, str> 的函数。如果你告诉函数返回"My message".into(),它就会查看类型:"My message"是str. 这是一个Borrowed的类型,所以它选择Borrowed(&'a B)。所以它就变成了Cow::Borrowed(&'static str)

而如果你给它一个format!("{}", "My message").into(),那么它就会查看类型。这次是一个String,因为format!创建了String。所以这次会选择 "Owned"。

下面是一个测试Cow的例子。我们将把一个数字放入一个函数中,返回一个Cow<'static, str>。根据这个数字,它会创建一个&strString。然后它使用.into()将其变成Cow。这样做的时候,它就会选择Cow::Borrowed或者Cow::Owned。那我们就匹配一下,看看它选的是哪一个。

use std::borrow::Cow;

fn modulo_3(input: u8) -> Cow<'static, str> {
    match input % 3 {
        0 => "Remainder is 0".into(),
        1 => "Remainder is 1".into(),
        remainder => format!("Remainder is {}", remainder).into(),
    }
}

fn main() {
    for number in 1..=6 {
        match modulo_3(number) {
            Cow::Borrowed(message) => println!("{} went in. The Cow is borrowed with this message: {}", number, message),
            Cow::Owned(message) => println!("{} went in. The Cow is owned with this message: {}", number, message),
        }
    }
}

这个打印:

1 went in. The Cow is borrowed with this message: Remainder is 1
2 went in. The Cow is owned with this message: Remainder is 2
3 went in. The Cow is borrowed with this message: Remainder is 0
4 went in. The Cow is borrowed with this message: Remainder is 1
5 went in. The Cow is owned with this message: Remainder is 2
6 went in. The Cow is borrowed with this message: Remainder is 0

Cow还有一些其他的方法,比如into_owned 或者 into_borrowed,这样如果你需要的话,你可以改变它。