Rust 隐式类型转换(Coercions)
类型会被隐式转换以适用特定上下文。这种转换主要集中在生存期和指针,通常是弱化类型。这种转换是目的主要是为提供编码便捷性,多数是无害的。
隐式转换规则
-
传递性原则: 如果
T1
可以隐式转换为T2
,并且T2
可以隐式转换为T3
,那么T1
可以隐式转换为T3
-
弱化指针:
&mut T
=>&T
*mut T
=>*const T
&T
=>*const T
&mut T
=>*mut T
-
大小无关化:如果
T
实现了CoerceUnsized<U>
,那么T
可以隐式转换为U
-
解引用:如果
T
解引用到U
(实现T: Deref<Target = U>
),那么可以通过表达式&*x
将类型为&T
的引用&x
转换为类型为&U
类型。 -
所有指针类型(包括智能指针如
Box
和Rc
)都实现了:CoerceUnsized<Pointer<U>> for Pointer<T> where T: Unsize<U>
,就是如果U
去掉大小这个属性后是类型T
,那么T
的所有指针类型都可以隐式转换为U
的相同指针类型。无大小系统自动实现。包含以下几种情况:[K; n]
=>[K]
K
=>dyn Trait
如果K: Trait
- 如果 T: Usize<U>,并且类型T在结构体中兄出现一次并且在最后一个域,那么
Foo<..., T, ...>
=>Foo<..., U, ...>
。如果T是被包裹的类型Bar<T>
,那么要求Bar<T>: Unsize<Bar<U>>
。
转换发生的地方(转换点)
隐式转换发生在明确表示了所需类型的地方,需要类型推断的地方不会有隐式转换发生。具体而言,将发生在以下地方:
假设有表达式
e
和所需类型U
- let、const、static 语句赋值,如
let x: U = e
- 函数调用,如
takes_a_U(e)
- 函数返回值,如
fn foo() -> U { e }
- 结构体字面量,如
Foo { some_u: e }
- 数组字面量,如
let x: [U; 10] = [e, ..]
- 元组字面量,如
let x: (U, ..) = (e, ..)
- 代码块的最后一个表达式,如
let x: U = { ..; e }
值得注意的点
如果所需类型是 Trait
时,类型转换的传递性原则无效。也就是说,如果一个转换点需要的类型是 TraitA
,如果 U: TraitA
,并且类型 T
=> U
成立,也不能用 T
做转换点的表达式。比如以下代码会报错:
trait Trait {}
fn foo<X: Trait>(t: X) {}
impl<'a> Trait for &'a i32 {}
fn main() {
let t: &mut i32 = &mut 0;
foo(t);
}
报错信息:
error[E0277]: the trait bound `&mut i32: Trait` is not satisfied
--> src/main.rs:9:5
|
9 | foo(t);
| ^^^ the trait `Trait` is not implemented for `&mut i32`
|
= help: the following implementations were found:
<&'a i32 as Trait>
note: required by `foo`
--> src/main.rs:3:1
|
3 | fn foo<X: Trait>(t: X) {}
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
即便 &mut i32
(类型T)可以转换为实现特性 Trait 的 &i32
(类型U),也不可以直接将 &mut i32
作为函数 foo
的参数。