Rust入门失败之Utility Traits

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::Sizedtrait, 这个trait没有任何方法和联合类型(associated type)

  • 不能实现自定义的Sized trait.

  • Sized trait只能用于绑定到类型参数, 也就是用于参数的类型声明(及检查), 例如 T: Sized不能用于其它用途, 这种trait 叫 marker trait

  • ?Sizedquestionably sized, 允许固定大小, 也允许非固定大小类型.

  • struct的最后一个字段允许是?Sized类型, 但如果这样, struct本身就变为了unsized.

  • 但如果写成泛型, 并传入一个Sized类型, 那么这个类型的struct仍然是Sized. 大小取决于泛型的参数类型:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    struct 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独立无关的一份拷贝.

  • Clonetrait是Sized的sub-trait, 所以Self类型必须是Sized

    1
    2
    3
    4
    5
    6
    trait 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 trait

  • clone方法不能失败(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
    4
    let 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

  • FromInto都会获取原变量的所有权, 转换后返回给调用者.

  • 二者是相反的过程, 且也是Sized的sub-trait:

    1
    2
    3
    4
    5
    6
    7
    trait 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
    4
    trait 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
    2
    let s: &str = "a";
    let ss: String = s.to_owned(); // ss is a new String clone from s(&str)
  • 通常实现ToOwnedtrait的都是一些borrow出的引用类型, 如str, 或[T].

  • 而它的联合类型Owned通常是可以转换为它的类型, 如String, Vec<T>.

Cow

  • 上面的ToOwned是立即把borrow来的引用转换生成一份Owned的拷贝. clone是有开销的, 有时无法确定是要处理引用还是获取所有权.

  • 另一方面, Cow实际就是一个智能指针, 实现了写时复制(C*opy-On-Write)*的功能.

    1
    2
    3
    4
    5
    6
    enum 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(): 会调用Bto_owned方法, 生成一份拷贝, 枚举值转为Owned.
    • into_owned(): 提取出一份Owned数据, 如果当前是Borrow也会调用to_owned转换. 提取意味着这个Cow变量调用后不再可用.

下面官方的例子很好解释这一点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::borrow::Cow;

fn abs_all(input: &mut Cow<i32>) {
for i in 0..input.len() {
let v = input[i];
if v < 0 {
// Clone into vector if not alread owned.
input.to_mut()[i] = -v;
}
}
}

// No clone occurs because `input` doesn't need to be mutated.
let slice = [0, 1, 2];
let mut input = Cow::from(&slice[..]);
abs_all(&mut input);

// Clone occurs because `input` needs to be mutated.
let slice = [-1, 0, 1];
let mut input = Cow::from(&slice[..]);
abs_all(&mut input);

// No clone occurs because `input` is already owned.
let mut input = Cow::from(vec![-1, 0, 1]);
abs_all(&mut input);