按照【来源】与【目标】的配对方式可以分成三类:
从【值】TypeA 至【值】TypeB
由【目标类型】TypeB实现From<TypeA> Trait。
impl From<TypeA> for TypeB
自从rustc 1.42+版本以后,由于From<T> Trait的【全覆盖实现blanket implementation(即,给·泛型类型·而不是·具体类型·实现Trait)】新特性,rustc会给【来源类型】TypeA自动提供Into<TypeB> Trait实现。即,
- 
徒手实现: impl From<TypeA> for TypeB
- 
买一送一: impl Into<TypeB> for TypeA
划算不划算,惊喜不惊喜?
另一方面,【类型转换】造成的副作用也包括:
- TypeA变量的【所有权转移】。即,在【类型转换】之后,原保存- TypeA实例的变量就“废”了。
- TypeB新实例被创建。即,这是有内存消耗成本的。其就是使用- TypeA实例的内部数据创建一个- TypeB实例。然后,对此曰为:“类型转换”。- 他们亏心不亏心呀? 
典型使用案例【错误处理】
将来自第三方库的【伪-错误类型】(即,这些类型被当作Error来用和放到Result<T, E>枚举类里,却没有实现Error Trait - 这是人该干的事吗?)自动转换成自定义的【真-错误类型】。
然后,我们才能够,对所有满足Error trait bound的类型,使用动态分派的Box<dyn Error>进行统一捕获处理。
于是,咱们的代码才有了那么点OOP抽象的意思。
期间,From<TypeA> Trait吸引开发者使用的重要噱头就是:?操作符能够自动
- 识别实现了From<TypeA> Trait的类型
- 和执行类型转换
代码看着好【优雅】,很适合有代码“洁癖”的程序员。
从【值】TypeA至【引用】&TypeB
我个人理解的分享
【TypeB的引用】既能够指向它自己类型的值,还可以指向另一个类型 【TypeA的值 】。即,
- TypeA既不被复制,也没被所有权转移 --- 这个- From<T> Trait明显不同。
- 同时,甩出一个既指向【TypeA值】的且“形状”又不同的【TypeB引用】
这又分成两种情况:
- 借入作为
- 仅引用
借入作为
要求TypeA与 TypeB以完全相同的方式实现了
- Eq Trait
- Ord Trait
- Hash Trait
这种类型转换模式被称为:TypeA被“借入作为”TypeB。
于是,需要给【来源类型】TypeA实现Borrow<T> Trait或BorrowMut<T> Trait:
- 
impl Borrow<TypeB> for TypeA
- 
impl BorrowMut<TypeB> for TypeA
由于
Borrow<T> Trait接收【泛型类型参数】,所以同一个【来源类型】TypeA能够被“装配”多个Borrow<T>实现块和同时被“借入作为”多个不同的【目标类型】TypeB1,TypeB2, ...】。
典型使用案例HashMap<String, V>
HashMap<String, V>既能接受String值作为【键】添加新【键-值对】,又能够接受&str引用作为【键】来检索/删除【键-值对】,正是因为String能够被借入作为str。
看一眼HashMap<K, V>中,insert与get两个成员方法的签名(特别是泛型类型参数的Trait Bound)就一目了然了:
- 
pub fn insert(&self, key: K, value: V) -> Option<V> where K: Hash + Eq;当 K为String时,接收String类型的【键】来添加新【键-值对】
- 
pub fn get<Q>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq + ?Sized;当 K为String时,因为K能够被借入作为Q,所以get成员方法就可以接收&str引用为【键】来检索【值】。
这一切都是因为有了
impl Borrow<str> for String
HashMap<K, V>这类用法曾经一度让我特别地困惑。我也下功夫读了不少官方文档,但可能是因为英文水平不足吧,始终云里雾里,理解与认识都不能自恰。直到精读了HashMap<K, V>源码看到了Borrow<T> Trait和再精读了Borrow<T> Trait的API手册,才算是有了那么点理解的感觉。
仅引用
对TypeA与TypeB是否与如何实现
- Eq Trait
- Ord Trait
- Hash Trait
没有限制。
只要给【来源类型】TypeA实现AsRef<T> Trait或AsMut<T> Trait即可。
- 
impl AsRef<TypeB> for TypeA
- 
impl AsMut<TypeB> for TypeA
典型使用案例-伪函数重载
利用Trait Bound,让函数在同一个形参槽位上既能接收String类型实参,也能接收&str类型的实参
fn test(s: impl AsRef<str>) -> ()
,仅只因为
- 
impl AsRef<str> for String
- 
impl AsRef<str> for str
从【引用】&TypeA 至【值】TypeB
这也分类两种模式:
- 刷复本模式
- 去引用模式(即,重载去引用操作符 *)
刷复本模式
要求TypeA实现ToOwned<Owned=T> Trait (Owned是“关联类型”)。ToOwned Trait 可以被看作是Clone Trait的**“全地形”版本**,因为Clone Trait仅只是允许同类型之间的“刷复本”,而ToOwned Trait允许跨类型之间“刷复本”(比如,从&TypeA引用克隆出TypeB实例)。
去引用模式
这是实现自定义【智能指针】的重要工具Deref<T> Trait和DerefMut<T> Trait。其是真正让你能够“触碰到”被引用的内存位置和修改被保存于那个内存位置上的数据。没有任何复本会被刷出来。
这个主题已经足够单独开讲,这里也就先不展开解释了。
评论区
写评论还没有评论