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);
    }
相关推荐
brrdg_sefg2 小时前
Rust 在前端基建中的使用
前端·rust·状态模式
m0_748230943 小时前
Rust赋能前端: 纯血前端将 Table 导出 Excel
前端·rust·excel
SomeB1oody10 小时前
【Rust自学】6.1. 定义枚举
开发语言·后端·rust
SomeB1oody10 小时前
【Rust自学】5.3. struct的方法(Method)
开发语言·后端·rust
itas1091 天前
Rust调用C动态库
c语言·rust·bindgen·bindings·rust c绑定
SomeB1oody1 天前
【Rust自学】5.1. 定义并实例化struct
开发语言·后端·rust
m0_748236111 天前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
SomeB1oody2 天前
【Rust自学】4.1. 所有权:栈内存 vs. 堆内存
开发语言·后端·rust
SomeB1oody3 天前
【Rust自学】4.2. 所有权规则、内存与分配
开发语言·后端·rust
SomeB1oody3 天前
【Rust自学】4.5. 切片(Slice)
开发语言·后端·rust