0x00 回顾
上一节讲解了字面量和常见一些运算符,还记得上节课最后留了一个练习题吗。
小明有5块钱,小红有3块5,小明和小红总共有多少钱呢?
你们成功运行出结果了吗,这节咱们介绍下Rust的类型转换。
0x01 类型转换(Type Cast)
与其它语言不同,在Rust中,整数和浮点数不能再一起做运算。先看下示例:
let o = 5; let p = 3.5; dbg!(o + p);
上面代码的输出结果是什么呢?如果你有C,Java等语言基础,那么你肯定会说上面的结果是8.5
。但是在Rust中编译器会报错,如下图所示。
cannot add a float to an integer
,不能把浮点数加到整数上。在Rust中不会帮你隐式的转换格式。
0x02 类型转换表达式(Type Cast Expressions)
语法
类型转换表达式 :
Expression
as
TypeNoBounds
类型转换使用as
操作符来表示。它可以将as
左边的类型强制转换为右边的类型。示例如下:
let o = 5; let q = o as f64; dbg!(q);
代码执行结果:
[src\main.rs:13] q = 5.0
不可变变量o
默认的类型是i32
,将其强制转换为f64
的类型复制给不可变变量q
,结果变为了5.0
,类型变为f64
浮点型。
0x03 旧题重解
咱们得知as
可以类型转换,那么题目的答案就显而易见了。
// 先将 o 转为 f64类型,再做加法 dbg!(o as f64 + p);
代码执行结果:
[src\main.rs:16] o as f64 + p = 8.5
现在的程序就不会报错了,结果也计算出来了。
0x04 类型转换规则(Coercion Rule)
as
可以被用于显示的强制类型转换。在数值进行强制转换时可能会存在精度问题。这里我再介绍下数值间强制转换的一些注意事项。
1、在两个相同长度大小的整数间进行有无符号强制转换,请注意符号位和整数范围。例如:u8
-> i8
下面是扩展知识。要理解这条规则,需要先明白在计算机中是如何存储数值的。
在计算机中存在有符号和无符号数两种数值,有符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。
上面提到了有符号数3个概念,原码,反码,补码。下面以+1
和-1
为例,简单解释下这三个概念,以8位整数类型为例。有符号数的最高位是符号位,0
表示正数,1
表示负数,因此+1
的原码就是0000 0001
,-1
的原码就是1000 0001
。对于正数来说,其原码,反码,补码都相同。负数的反码,则是对除符号位的每一位都取反(有关取反操作上节已经介绍了)。那么-1
的反码就是1111 1110
。补码就是在反码的基础上再加1。-1
的补码就是1111 1111
。如果反码在加1操作后,8位变为了9位,则将会舍弃最高位。
Rust强转的示例代码如下:
let a: i8 = 1; let b: i8 = -1; println!("[有符号数] {} 转为[无符号数]: {}, 转换后的二进制为 {:08b}", a, a as u8, a as u8); println!("[有符号数] {} 转为[无符号数]: {}, 转换后的二进制为 {:08b}", b, b as u8, b as u8); let a: u8 = 1; let b: u8 = 255; println!("[无符号数] {} 转为[有符号数]: {}, 转换后的二进制为 {:08b}", a, a as i8, a as i8); println!("[无符号数] {} 转为[有符号数]: {}, 转换后的二进制为 {:08b}", b, b as i8, b as i8);
代码执行结果:
[有符号数] 1 转为[无符号数]: 1, 转换后的二进制为 00000001 [有符号数] -1 转为[无符号数]: 255, 转换后的二进制为 11111111 [无符号数] 1 转为[有符号数]: 1, 转换后的二进制为 00000001 [无符号数] 255 转为[有符号数]: -1, 转换后的二进制为 11111111
2、较长位数的整数转换为较短位数的整数时会发生截断。例如:i16
-> i8
将16位整数300转为8位整数会发生什么?计算机会将300的二进制高位舍弃,只保留低位,这就是截断,会发生精度损失。示例如下:
let a: i16 = 300; println!("{} 的 二进制为:{:016b}", a, a); // 发生数据截断,保留了低位,截断了高位 println!("16位整数 {} 转为 8位整数:{},其二进制为:{:016b}", a, a as i8, a as i8);
代码执行结果:
300 的 二进制为:0000000100101100 16位整数 300 转为 8位整数:44,其二进制为:0000000000101100
3、较短位数的整数转换为较长位数的整数时会有下面两种情况。例如:i8
-> i16
- 如果该值是无符号数,则高位使用
0
填充 - 如果该值是有符号数,则高位使用符号数填充
示例代码:
let a: u8 = 45; // a 是8位整数,但是输出时的格式我加了0填充,可以忽略前8位的显示 println!("8位无符号整数 {} 的 二进制为:{:016b}", a, a); println!("8位无符号整数 {} 的 转为16位整数的二进制为:{:016b}", a, a as u16); let b: i8 = -121; // b 是8位整数,但是输出时的格式我加了0填充,可以忽略前8位的显示 println!("8位有符号整数 {} 的 二进制为:{:016b}", b, b); println!("8位有符号整数 {} 的 转为16位整数的二进制为:{:016b}", a, b as i16);
代码执行结果:
8位无符号整数 45 的 二进制为:0000000000101101 8位无符号整数 45 的 转为16位整数的二进制为:0000000000101101 8位有符号整数 -121 的 二进制为:0000000010000111 8位有符号整数 45 的 转为16位整数的二进制为:1111111110000111
0x05 小结
本篇文章仅仅是介绍了3个类型转化规则,还有6个规则没有介绍,由于篇幅过长不适合阅读,就分成两篇文章。小伙伴们不要忘记了......
未完待续,源码将在下节提供...........