rust - 理解borrow trait
简介
borrow trait
是处理借用(即其它语言中的引用)的 trait,变量的所有权不会转移.泛型定义如下:
pub trait Borrow<Borrowed: ?Sized> {
/// Immutably borrows from an owned value.
fn borrow(&self) -> &Borrowed;
}
其中包含一个 borrow(&self)
的方法,将变量的类型 T
转换为 &Borrowed
类型.
默认实现
rust 提供了类型 T
的默认实现,如下:
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
的默认实现,如下
#[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
,如下:
impl Borrow<str> for String {
#[inline]
fn borrow(&self) -> &str {
&self[..]
}
}
即将 String
类型转换为了 &str
类型.
可以自己对类型实现 Borrow trait
,如下:
#[allow(dead_code)]
struct Person {
name: String,
age: u8,
}
impl Borrow<str> for Person {
fn borrow(&self) -> &str {
self.name.as_str()
}
}
实现Borrow
的一个要求是,借用值的Hash
、Eq
和Ord
与拥有值的Hash
、Eq
和Ord
相等。这里可以解释为,如果两个用户 Person
的借用值 name
相同,则认为是同一个人.
使用场景
1. 对函数参数进行类型扩展
#[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()
, 如下
#[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
,如下:
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);
}