Drop
自定义drop方法在默认销毁前调用执行
如果实现了Drop Trait, 则不能实现Copy Trait.
如果实现了Copy Trait, 则意味这个类型可以通过byte-for-byte复制获得一份独立的数据拷贝, 那么drop两份相同的数据通常是一个错误.
std有一个全局的
drop
方法, 比较fancy: 通过参数获得所有权, 然后直接是个空函数, 函数返回就释放了实参的变量.1
fn drop<T>(_x: T) { }
Sized
Rust不能在变量里保存unsized的值, 也不能把unsize的值作为参数.
所有固定大小的类型都实现里
std::marker::Sized
trait, 这个trait没有任何方法和联合类型(associated type)不能实现自定义的Sized trait.
Sized trait只能用于绑定到类型参数, 也就是用于参数的类型声明(及检查), 例如
T: Sized
不能用于其它用途, 这种trait 叫 marker trait?Sized
叫 questionably sized, 允许固定大小, 也允许非固定大小类型.struct的最后一个字段允许是
?Sized
类型, 但如果这样, struct本身就变为了unsized.但如果写成泛型, 并传入一个Sized类型, 那么这个类型的struct仍然是Sized. 大小取决于泛型的参数类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14struct RcBox<T: ?Sized> {
rec_count: usize,
value: T,
}
// Sized type
let boxed_lunch: RcBox<String> = RcBox {
ref_count: 1,
value: "lunch".to_string()
}
use std::fmt::Display;
// UnSized type
let boxed_displayable: &RcBox<Display> = &boxed_lunch;
Clone
clone
方法必须返回和self
独立无关的一份拷贝.Clone
trait是Sized的sub-trait, 所以Self类型必须是Sized1
2
3
4
5
6trait Clone: Sized {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) {
*self = source.clone()
}
}通常情况下clone的成本比较高, 但是对于
Rc<T>
和Arc<T>
这类的类型, Rust的对它们的clone只是简单的增加计数.通常尽可能使用
clone_from
来减少clone开销, 这会允许一些优化. 例如, String的clone, 被赋值的String如果capacity够大, 可以不需要释放内存, 直接把源的内容拷贝过来.如果所有的字段都实现了Clone, 那么
struct
可以加上属性:#[derive(Clone)]
自动实现Clone traitclone
方法不能失败(infallible), 对于std::fs::File
这样的类型, 有try_clone
方法, 返回std::io::Result<File>
Copy
之前文章有介绍过, 如果数据类型持有一些需要销毁的资源(文件句柄、堆上数据等), 则不允许实现Copy trait.
1
trait Copy: Clone { }
Rust对于实现了Copy trait的类型进行赋值, 不会Move原变量的数据(所有权), 而是将原对象做浅拷贝一份给目标变量. 例如简单的数据类型 i32等.
AsRef<T> & Borrow<T>
AsRef<T>
和Borrow<T
一样, 返回的都是引用, 但是有一个区别:Borrow
保证对同一数据类型Borrow出不同的T都有相同哈希值, 而AsRef没有这类保证, 也就是说对于一个String, 下面三个AsRef返回的是三个哈希值不同的引用:1
2
3
4let s = String::from("/usr/bin/bash");
let ref1: &Path = s.as_ref();
let ref2: &[u8] = s.as_ref();
let ref3: &str = s.as_ref();
From and Into
From
和Into
都会获取原变量的所有权, 转换后返回给调用者.二者是相反的过程, 且也是
Sized
的sub-trait:1
2
3
4
5
6
7trait Into<T>: Sized {
fn into(self) -> T;
}
trait From<T>: Sized {
fn from(T) -> Self; // 静态方法
}如果自定义类型的构造只有一个参数, 那么可以写一个
from
方法实现From, 然后Rust会自动帮你实现一个Into trait. 这个Trait有个好处可以访问到一些类型的内部数据, 而又不破坏数据原有内容. 例如: String类型不允许直接修改内部的UTF-8字节, 但是通过
Into<Vec<u8>>
, 我们可以对String数据进行其它操作, 如果压缩等等.
ToOwned
如果拿到一个类型引用(假设这个类型实现了Clone Trait), 想对它进行
Clone
, 而在引用类型上无法调用clone
, 例如&str
上无法调用clone
, 因为返回的str
不是Sized
类型. 因此需要有个方法获转换到原类型数据拷贝, 从而获取到所有权(注意⚠️, 要求原类型实现Clone Trait, 返回的是clone出来的数据, 是原数据的一份拷贝, 和原数据没有半毛钱关系了).1
2
3
4trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}返回的是实现了
Borrow<Self>
的类型. 可以这么理解, 可以从实现了Borrow<Self>
的类型上borrow到&Self
, 那么反过来可以从&self
上获得到原类型数据. 例如:Vec<T>
实现了Borrow<[T]>
的trait, 可以borrow到&[T]
, 那么反过来可以从&[T]
的实例获取到一份Vec<T>
类型的拷贝.Self
是[T]
, Owned类型是Vec<T>
1
2let s: &str = "a";
let ss: String = s.to_owned(); // ss is a new String clone from s(&str)通常实现
ToOwned
trait的都是一些borrow出的引用类型, 如str
, 或[T]
.而它的联合类型
Owned
通常是可以转换为它的类型, 如String
,Vec<T>
.
Cow
上面的
ToOwned
是立即把borrow
来的引用转换生成一份Owned
的拷贝. clone是有开销的, 有时无法确定是要处理引用还是获取所有权.另一方面, Cow实际就是一个智能指针, 实现了写时复制(C*opy-On-Write)*的功能.
1
2
3
4
5
6enum Cow<'a, B: ?Sized + 'a>
where B: ToOwned
{
Borrow(&'a B),
Owned(<B as ToOwned>::Owned),
}Cow有个
from
方法, 如果Cow拿到是一个普通的共享引用类型, 那么Cow枚举当前值就是Borrow
. 例如, 传入&[T]
, 那边B 就是[T]
.Cow不会自动转为Owned类型. 也就是不会把引用B (
str
或[T]
之类)自动复制出一份. 除非显式调用它的两个方法:to_mut()
: 会调用B 的to_owned
方法, 生成一份拷贝, 枚举值转为Owned
.into_owned()
: 提取出一份Owned数据, 如果当前是Borrow
也会调用to_owned
转换. 提取意味着这个Cow变量调用后不再可用.
下面官方的例子很好解释这一点:
1 | use std::borrow::Cow; |