原文链接:https://doc.rust-lang.org/nomicon/casts.html

显式类型转换

显式类型转换是强制类型转换的超集:所有的强制类型转换都可以通过显式转换的方式主动触发。但有一些场景只适用于显式转换。强制类型转换很普遍而且通常无害,但是显式类型转换是一种“真正的转换“,它的应用就很稀少了,而且有潜在的危险。因此,显式转换必须通过关键字as主动地触发:expr as Type

真正的转换一般是针对裸指针和基本数字类型的。虽然说过它们存在风险,但是在运行期却是很稳定的。如果类型转换操作触发了一些奇怪的边界场景,Rust并不会给出任何提示。转换仍然会被认为是成功的。这就要求显式类型转换必须在类型层面是合法的,否则会在编译期被拒绝。比如,7u8 as bool不会编译成功。

也就是说,显式类型转换不属于非安全(unsafe)行为,因为仅凭转换操作是不会违背内存安全性的。比如,将整型转换为裸指针很容易导致可怕的后果。但是,创建一个指针这个行为本身是安全的,而真正使用裸指针的操作则必须被标为unsafe

以下是所有显式类型转换的情况。简单起见,我们用*表示*const或者*mut,用integer表示任意整数基本类型:

  • *T as *U,其中T, U: Sized
  • *T as *U,TODO:明确unsize的情况
  • *T as integer
  • integer as *T
  • number as number
  • 无成员枚举as integer
  • bool as integer
  • char as integer
  • u8 as char
  • &[T; n] as *const T
  • fn as *T,其中T: Sized
  • fn as integer

注意,裸slice转换后长度会改变,比如*const [u16] as *const [u8]创建的slice只包含原本一半的内存。

显示类型转换不是可传递的,也就是说,即使e as U1 as U2是合法的表达式,也不能认为e as U2就一定是合法的。

对于数字类型的转换,如下几点需要注意:

  • 相同大小的整型互相转换(比如i32->u32)是一个no-op
  • 大尺寸的整型转换为小尺寸的整型(比如u32->u8)会被截断
  • 小尺寸的整型转换为大尺寸的整型(比如u8->u32)
    • 如果源类型是无符号的,将会补零
    • 如果源类型是有符号的,将会有符号补零
  • 浮点类型转换为整型会舍去浮点部分,并且当浮点数超出整数范围时会产生“饱和转换”
    • 太大的浮点数会变成可能的最大整数
    • 太小的浮点数会产生可能的最小整数
    • NaN 产生零
  • 整型转换为浮点类型会产生这个整型的浮点型表示,
  • f32转换为f64可以无损失地完美转换,必要的时候做舍入(舍入到最近的可能取值,距离相同的取偶数)
  • f64转换为f32会生成最近可能值(舍入到最近的可能取值,距离相同的取偶数)