rust - 理解borrow trait

简介

borrow trait 是处理借用(即其它语言中的引用)的 trait,变量的所有权不会转移.泛型定义如下:

rust 复制代码
pub trait Borrow<Borrowed: ?Sized> {
    /// Immutably borrows from an owned value.
    fn borrow(&self) -> &Borrowed;
}

其中包含一个 borrow(&self)的方法,将变量的类型 T 转换为 &Borrowed 类型.

默认实现

rust 提供了类型 T 的默认实现,如下:

rust 复制代码
impl<T: ?Sized> Borrow<T> for T {
    fn borrow(&self) -> &T {
        self
    }
}

impl<T: ?Sized> Borrow<T> for &T {
    fn borrow(&self) -> &T {
        &**self
    }
}

impl<T: ?Sized> Borrow<T> for &mut T {
    fn borrow(&self) -> &T {
        &**self
    }
}

这里创建一个自定义类型来说明 borrow 的默认实现,如下

rust 复制代码
    #[test]
    fn test_default_borrow() {
        struct MyType;

        let my_type = MyType;
        let _: &MyType = my_type.borrow();
        let _: &MyType = (&my_type).borrow();

        let my_type = &MyType;
        let _: &MyType = my_type.borrow();
        let _: &MyType = (*my_type).borrow();

        let my_type = &mut MyType;
        let _: &MyType = my_type.borrow();
        let _: &MyType = (*my_type).borrow();
    }

自定义 borrow trait

String 类型就实现了自己的 borrow trait,如下:

rust 复制代码
impl Borrow<str> for String {
    #[inline]
    fn borrow(&self) -> &str {
        &self[..]
    }
}

即将 String 类型转换为了 &str 类型.

可以自己对类型实现 Borrow trait,如下:

rust 复制代码
#[allow(dead_code)]
struct Person {
    name: String,
    age: u8,
}

impl Borrow<str> for Person {
    fn borrow(&self) -> &str {
        self.name.as_str()
    }
}

实现Borrow的一个要求是,借用值的HashEqOrd与拥有值的HashEqOrd相等。这里可以解释为,如果两个用户 Person 的借用值 name 相同,则认为是同一个人.

使用场景

1. 对函数参数进行类型扩展

rust 复制代码
    #[test]
    fn test_origin_check() {
        fn origin_check(s: &String) {
            assert_eq!("Hello", s);
        }

        let s = "Hello".to_string();
        origin_check(&s);
    }

例如,上面的函数origin_check接受 String 类型的参数,但是如果想复用这个函数,需要将其它类型的参数也传给origin_check函数该怎么实现呢?

先修改成第一个版本, 添加s.borrow(), 如下

rust 复制代码
    #[test]
    fn test_origin_check_1() {
        fn origin_check(s: &String) {
            let borrowed: &str = s.borrow();
            assert_eq!("Hello", borrowed);
        }

        let s = "Hello".to_string();
        origin_check(&s);
    }

这样只要确保类型 s,都实现了 borrow()方法,且返回值的类型都为 &str就可行了.

再修改成第二个版本, 这样就可以将任何实现了Borrow<str>类型的变量作为参数传递给函数check,如下:

rust 复制代码
    fn check<K>(s: K)
    where
        K: Borrow<str>,
    {
        let borrowed: &str = s.borrow();
        assert_eq!("Hello", borrowed);
    }

    #[test]
    fn test_borrow_as_param() {
        // 支持 String 类型,因为 String 实现了 Borrow trait,可以转换为 &str
        let s = "Hello".to_string();
        check(s);

        // 支持 &str 类型,因为 rust 实现了类型 T 的默认 borrow 实现, 转换为 &str 类型
        let s = "Hello";
        check(s);

        // 支持 Person 类型,因为 Person 实现了 Borrow trait, 可以转换为用户名属性的 &str 类型
        let s = Person { name: "Hello".to_string(), age: 18 };
        check(s);
    }
相关推荐
该用户已不存在5 小时前
Mojo vs Python vs Rust: 2025年搞AI,该学哪个?
后端·python·rust
大卫小东(Sheldon)7 小时前
写了一个BBP算法的实现库,欢迎讨论
数学·rust
echoarts12 小时前
Rayon Rust中的数据并行库入门教程
开发语言·其他·算法·rust
ftpeak21 小时前
从零开始使用 axum-server 构建 HTTP/HTTPS 服务
网络·http·https·rust·web·web app
咸甜适中1 天前
rust语言 (1.88) 学习笔记:客户端和服务器端同在一个项目中
笔记·学习·rust
咸甜适中1 天前
rust语言 (1.88) egui (0.32.2) 学习笔记(逐行注释)(二十八)使用图片控件显示图片
笔记·学习·rust·egui
huli33201 天前
pingora_web:首款基于 Cloudflare Pingora 的企业级 Rust Web 框架
rust
Pomelo_刘金1 天前
如何优雅地抽离 Rust 子工程:以 rumqttd 为例
rust
几颗流星1 天前
Rust 常用语法速记 - 错误处理
后端·rust
向上的车轮2 天前
如何用 Rust 重写 SQLite 数据库(二):是否有市场空间?
数据库·rust·sqlite