借用
有很多特征可以用来获取借用,包括了 Borrow
BorrowMut
ToOwned
Cow
AsRef
等。
AsRef
其定义在 std::convert
中,相似的还有 AsMut
Into
From
等特征。
pub trait AsRef<T: ?Sized> {
fn as_ref(&self) -> &T;
}
pub trait AsMut<T: ?Sized> {
fn as_mut(&mut self) -> &mut T;
}
简单来说:
- 如果
U
实现了AsRef<T>
,那么as_ref()
可以实现&U
到&T
的转换。 - 如果
U
实现了AsMut<T>
,那么as_ref()
可以实现&U
到&mut T
的转换。
另外,为了方便使用,还实现了如下的泛型。
// As lifts over &
impl<T: ?Sized, U: ?Sized> AsRef<U> for &T
where
T: AsRef<U>,
{
#[inline]
fn as_ref(&self) -> &U {
<T as AsRef<U>>::as_ref(*self)
}
}
// As lifts over &mut
impl<T: ?Sized, U: ?Sized> AsRef<U> for &mut T
where
T: AsRef<U>,
{
#[inline]
fn as_ref(&self) -> &U {
<T as AsRef<U>>::as_ref(*self)
}
}
// AsMut lifts over &mut
impl<T: ?Sized, U: ?Sized> AsMut<U> for &mut T
where
T: AsMut<U>,
{
#[inline]
fn as_mut(&mut self) -> &mut U {
(*self).as_mut()
}
}
也就是说:
- 如果
T
实现了AsRef<U>
,那么&T
就实现了AsRef<U>
。 - 如果
T
实现了AsRef<U>
,那么&mut T
就实现了AsRef<U>
。 - 如果
T
实现了AsMut<U>
,那么&mut T
就实现了AsMut<U>
。
以如下的 std::fs::File::open()
函数为例,该函数期望的参数是 &Path
类型,而实际上传递 String
str
类型也都可以。
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
OpenOptions::new().read(true).open(path.as_ref())
}
在 Path
的实现 std/path.rs
中可以看到如下的实现,这里是通过新建对象实现。
impl AsRef<Path> for String {
fn as_ref(&self) -> &Path {
Path::new(self)
}
}
Borrow
很多时候不同场景会对类型进行一些封装,例如 Box<T>
Rc<T>
以及 String
和 str
等,除了表面的数据类型之外,还有其底层的数据类型,那么当获取 (或者 Borrow) 其底层数据时,是否要保持相同的 Eq
Ord
Hash
等特征。
也就是说,对于 x.borrow() == y.borrow()
和 x == y
的结果应该相同。
Sized
用来表示在编译阶段即可确定大小的类型,除此之外还有 ?Sized
表示同时支持动态大小类型,也就是 Dynamically Size Types, DSTs 类型,常见的有:切片 &[T]
、字符串切片 &str
、Trait Object &dyn Trait
以及包含上述 DST 的类。
在 Rust 中,引用或者原始指针采用 usize
大小的内存保存,称为瘦指针 (Thin Pointer),而 DST 类型也被称为胖指针 (Fat Pointer) 或者 Pointer Metadata,会包含额外 usize
大小的指针指向元数据。
不同类型保存的元数据信息不同,例如切片 (含字符串切片) 保存的是长度,而 Trait Object 保存的是虚拟表。
#![feature(ptr_metadata)]
#![allow(dead_code)]
use std::ptr::metadata;
trait Shape {
fn area(&self) -> f64;
}
struct Rectangle {
width: f64,
height: f64,
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
fn main() {
let slice: &[u8] = &[1, 2, 3];
assert_eq!(metadata(slice), 3);
let string = "Hello";
assert_eq!(metadata(string), 5);
struct Wrapper<T: ?Sized> {
value: u32,
data: T,
}
let wrapper: &Wrapper<[u8]> = &Wrapper {
value: 0,
data: [0, 1, 2],
};
assert_eq!(metadata(wrapper), 3);
let thin: u8 = 2;
assert_eq!(metadata(&thin), ());
let rect: &dyn Shape = &Rectangle {
width: 10.0,
height: 2.0,
};
//assert_eq!(metadata(&thin), DynMetadata::<Shape>);
println!("{:?}", metadata(rect));
}
其它
#[allow(dead_code)]
允许部分代码未调用,当采用#!
时表示crate
内,通常在测试、演示场景中使用。