Rust 智能指针 III

你应该知道的常见智能指针

技术
技术Rust标准库编程语言特性智能指针

2026-04-01

String

Cow<'a, B>

ToOwnedBorrow 特征

当只有一个不可变引用,却想对其进行修改或获取所有权时,大家很可能会使用 Clone 特征来从借用数据克隆出拥有所有权的数据。但是某种意义上 Clone 是狭隘的:它只提供从 &T 转换为 T 的功能。而 ToOwnedClone 进行了泛化。即,它可以将 &T 转换为 U 类型。下面是 ToOwned 的定义:

pub trait ToOwned {
    type Owned: Borrow<Self>;

    // Required method
    fn to_owned(&self) -> Self::Owned;

    // Provided method
    fn clone_into(&self, target: &mut Self::Owned) { ... }
}

根据定义,T 若实现了 ToOwned,那么 &T 能通过调用 to_owned 转换为 Owned 类型。这个 Owned 是一个实现了 Borrow<Self> 特征的类型。Borrow 又是什么玩意?

`Borrow<Borrowed>` 特征

经常能见到标准库中一些类型为另一些类型提供了额外的功能扩展,而这些功能可能会带来性能开销。就比如 String 类型在原生类型 str 的基础上增加了一些字符串的可扩展能力,代价就是需要维护一些对于简单不可变字符串来说并不必要的额外信息。

这些提供扩展功能的类型通常会通过返回对底层数据类型的引用来提供访问能力。而 Borrow 特征就是干这件事的,它声明了某个类型可以返回的底层数据是何种类型,并提供了访问方法。

Borrow 特征定义如下:

pub trait Borrow<Borrowed>
where
    Borrowed: ?Sized,
{
    // 需要实现的方法
    fn borrow(&self) -> &Borrowed;
}
// 一个类型通过实现 `Borrow<T>` 来表达它们可以被借用为类型 `T`
// 并通过该特征的 `borrow` 方法返回 `&T`

在官方文档中,你可以看到很多老面孔:

  • String 实现了 Borrow<str>

  • Box<T>Rc<T>Arc<T> 都实现了 Borrow<T>

如果一个类型还希望支持以可变方式借用,从而允许修改底层数据的话,那么它还可以额外实现 BorrowMut<T>

综上所述,T 若想实现 ToOwned,需要一个实现了 Borrow<T> 的关联类型,这意味着有个类型能被借用为 T。而 T 实现 ToOwned 又表明 T 能被转为一个持有所有权的类型。

所以这两个特征的作用实际是相互的。

让我们看看现实是不是这样:

str 类型实现了 ToOwned,且关联类型 OwnedString,而上面提过 String 实现了 Borrow<str>,因此我们可以对 &str 调用 to_owned 来转为一个 String

let slice = "Hello world";
assert_eq!(slice.to_owned(), slice.to_string());

还真是!

看完了 ToOwnedBorrow 特征,下面可以进入正题了。

写时克隆的 Cow

Cow<'a, B> 是一种提供写时克隆(Clone-on-Write)功能的智能指针。它可以封装借用数据并提供对其的不可变访问,同时在需要修改或获得所有权时按需惰性地克隆数据。

该类型设计通过 Borrow 特征来支持通用的借用数据。

Cow 实现了 Deref,这意味着你可以直接对其封装的数据 B 调用接收 &B 的方法。如果需要修改数据,to_mut 方法将获取一个指向自有值的可变引用,并在必要时进行克隆。

如果你需要引用计数指针,请注意 Rc::make_mutArc::make_mut 同样可以提供写时克隆的功能。