按照【来源】与【目标】的配对方式可以分成三类:
从【值】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
。其是真正让你能够“触碰到”被引用的内存位置和修改被保存于那个内存位置上的数据。没有任何复本会被刷出来。
这个主题已经足够单独开讲,这里也就先不展开解释了。
评论区
写评论还没有评论