Cow

Cow说明

Cow 适合“多数情况下借用,少数情况下修改”的场景
Cow 的核心价值是:尽量不分配,只有必要时才分配

  • 能借用就借用,需要修改时才拷贝成拥有的数据。

基本类型

use std::borrow::Cow;

Cow<'a, str>

两种状态

// 不分配内存,返回借用
Cow::Borrowed(&'a str)

// 在需要时才创建
Cow::Owned(String)

问题说明

有时直接返回借用的字符串,不分配内存
有时需要生成新的字符串

// 这里即使只是返回原始 name,也必须 .to_string(),会发生分配。
// 用 Cow 可以避免不必要的分配
fn check_user_name(name: &str) -> String {
    if name == "admin" {
        let s = format!("{}-{}", name, "hello");
        s
    } else {
        name.to_string()
    }
}

fn main() {
    let res = check_user_name("demo");
    println!("res: {}", res);
}

解决方法

  • Cow<'_, str>中'_让编译器自动推断这个生命周期
    • 等价于fn check_user_name<'a>(name: &'a str) -> Cow<'a, str>
use std::borrow::Cow;

fn check_user_name(name: &str) -> Cow<'_, str> {
    if name == "admin" {
        Cow::Owned(format!("{}-{}", name, "hello"))
    } else {
        Cow::Borrowed(name)
    }
}

fn main() {
    let res: Cow<'_, str> = check_user_name("demo");
    println!("res: {}", res);
}
res: demo

解释说明

Cow<'a, str> 里 'a 是什么

'a 是生命周期。
它表示:这个 Cow 里如果是借用数据,那么它借用的那份数据至少要活到 'a 这么久。

// 输入的 name 活多久
// 返回的 Cow 里如果借用了它,也只能活这么久
fn f<'a>(name: &'a str) -> Cow<'a, str>

为什么不是Cow<'a, &str>

因为&str本身已经是一个引用类型了

  • Cow<'a, &str>
    • Borrowed会是&&str
    • Owned也会对应&str的owned类型,但这不符合 Cow 的常规用途
// B 是底层类型
// Borrowed 里才会再加一层 &

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

为什么不是Cow<'a, String>

因为 Cow 不是“借用 String 或拥有 String”这么简单。
它是: 借用某种数据 B,或者拥有它对应的 Owned 类型。

  • 对于 str:
    • 借用时是 &str
    • 拥有时是 String
// Borrowed 存的是 &'a B
// Owned 存的是 B::Owned
Cow<'a, B>

Cow类型

Cow 可以用于很多类型,只要这个类型实现了ToOwned

&str/String

Cow<'a, str>

= Borrowed(&'a str)
= Owned(String)

数组/切片:

Cow<'a, [u8]>

= Borrowed(&'a [u8])
= Owned(Vec<u8>)

Path/PathBuf

Cow<'a, std::path::Path>

= Borrowed(&'a std::path::Path)
= Owned(std::path::PathBuf)

OsStr/OsString

Cow<'a, std::ffi::OsStr>

= Borrowed(&'a std::ffi::OsStr)
= Owned(std::ffi::OsString)

Cow常用方法

to_mut()

to_mut()会在当前是Borrowed时自动克隆成Owned,然后返回可变引用

use std::borrow::Cow;

fn check_user_name(name: &str) -> Cow<'_, str> {
    if name == "admin" {
        Cow::Owned(format!("{}-{}", name, "hello"))
    } else {
        Cow::Borrowed(name)
    }
}

fn main() {
    let mut res: Cow<'_, str> = check_user_name("demo");
    let res1: &mut String = res.to_mut();
    res1.push('a');
    println!("res1: {}", res1);
}
res1: demoa

into_owned()

into_owned()会把Cow转成真正拥有的数据,Borrowed会clone,Owned直接拿走。

use std::borrow::Cow;

fn check_user_name(name: &str) -> Cow<'_, str> {
    if name == "admin" {
        Cow::Owned(format!("{}-{}", name, "hello"))
    } else {
        Cow::Borrowed(name)
    }
}

fn main() {
    let res: Cow<'_, str> = check_user_name("demo");
    let mut res1: String = res.into_owned();
    res1.push('b');
    println!("res: {}", res1);
}
res1: demob

as_ref()

如果只是想拿引用,不关心 owned / borrowed

use std::borrow::Cow;

fn check_user_name(name: &str) -> Cow<'_, str> {
    if name == "admin" {
        Cow::Owned(format!("{}-{}", name, "hello"))
    } else {
        Cow::Borrowed(name)
    }
}

fn main() {
    let res: Cow<'_, str> = check_user_name("demo");
    let res1: &str = res.as_ref();
    println!("res1: {}", res1);
}
res1: demo
posted @ 2026-09-02 12:07  lxd670  阅读(29)  评论(0)    收藏  举报