讲动人的故事,写懂人的代码
2.6.6 用as
进行类型转换:显式而简洁的语法
贾克强:“大家在查看Rust代码时,可能会注意到这一句。在这里,如果我们不使用as i32
,编译器会报错,因为它在u32
中找不到abs()
方法。这是因为prev
和sum_of_two_dice
都是u32
类型,u32
类型并不支持abs()
方法。所以,我们需要使用as
关键字把它们都转换成支持abs()
方法的i32
类型。”
let previous_diff = (prev as i32 - sum_of_two_dice as i32).abs();
“在 Rust 中,我们使用 as
关键字进行类型转换,语法简洁明了,能清楚地表明类型转换的意图。这种方式让代码的可读性更高,更容易理解和维护哦~”
“不过,要注意一下,在 Rust 中,使用 as
关键字进行类型转换时,并不会自动检查转换过程中是否会有溢出、数据丢失或精度丧失的情况。这种转换是直接将一个类型的值转换为另一个类型的值,所以可能会导致截断或数据丢失。在使用 as
时,我们需要格外小心哦。”
艾极思用表格将三种语言的类型转换进行了对比。
类型转换 | Rust的as | Java类型转换 | C++类型转换 |
---|---|---|---|
用途 | 转换整数、浮点数、指针类型 | 转换基本数据类型、对象引用类型 | 转换基本数据类型、指针、引用、对象 |
语法 | <表达式> as <目标类型> | (<目标类型>)<表达式> | (<目标类型>)<表达式>(C风格),<类型转换操作符><目标类型>(<表达式>)(C++风格) |
代码示例 | let x: i32 = 42;let y: u8 = x as u8; // 将 i32 类型的 x 转换为 u8 类型的 y | int a = 10;double b = (double) a; // 将 int 类型的 a 转换为 double 类型的 b | int x = 5;double y = (double) x; // C风格转换,将 int 类型的 x 转换为 double 类型的 y int x = 5; double y = static_cast(x); // C++风格转换,使用 static_cast 进行类型转换 |
检查 | 无类型检查 | 有类型检查(运行时和编译时) | 有类型检查(dynamic_cast),无类型检查(static_cast等) |
转换风险 | 可能导致数据丢失或截断 | 对象类型转换可能抛ClassCastException,基本类型可能导致数据丢失 | C风格转换不安全,static_cast等有较高风险,dynamic_cast较安全 |
适用场景 | 基本类型、指针转换 | 基本类型转换,类型安全的对象转换 | 基本类型转换,指针和引用的转换,特殊场景使用不同转换 |
扩展性 | 只能用于标量类型 | 支持对象引用类型的多态转换 | 支持更多类型的转换和控制 |
安全性 | 转换不检查类型,容易出错 | 类型安全,检查严格 | 多种类型转换方式可选,灵活但复杂 |
2.6.7 在loop
里的String::new()
是否会增大内存占用?
赵可菲:“在那个一直转啊转的loop里,那句let mut guess = String::new();
,会不会让上一轮新弄出来的字符串实例默默等待被垃圾回收,这样内存占用岂不是要增加?”
贾克强友善地说:“Java程序员有这种担心,是很自然的哦。在Java中,内存管理主要依赖于垃圾回收器,这意味着程序员不需要手动管理内存,垃圾回收器会自动回收不再使用的对象的内存。”
“但在Rust里,没有Java那样的垃圾回收机制(garbage collection),所以不会出现你说的情况的。在Rust中,内存管理主要通过所有权(ownership)系统来实现。当变量超出其作用域(scope)时,它所占用的内存会立即被释放。”
“比如,在咱们的Rust代码中,虽然每次循环都会创建一个新的字符串实例guess
,但是每次循环结束后,这个字符串实例就会超出其作用域,随即实例会被销毁,内存也就被释放。这样就不会导致内存占用不断增加的问题。”
“Rust的这种内存管理方式比垃圾回收更为高效,因为它避免了垃圾回收器在运行时对内存的额外开销。通过所有权系统和作用域管理,Rust能够确保内存的高效使用。”
席双嘉:“事实上,Rust的基于所有权的内存管理机制,能够有效地实现超出作用域的变量的内存自动释放。相比之下,C++的内存安全主要靠程序员手动管理,而Rust无疑提供了更高的便利性。然而,在一个loop
里,循环导致的字符串实例的多次创建和销毁,可能会产生一定的系统开销。那么,如果我们把这句let mut guess = String::new();
放到loop
之外,我相信这将对性能产生积极的影响。”
贾克强向席双嘉竖起大拇指,笑眯眯地赞扬道:“果然是追求极致性能的C++高手!说得太好了。的确,将字符串实例的创建放到循环之外,能有效减少这种开销。但是,也得记得在每次循环开始时确保字符串实例是空的,否则程序会出错哦。因此,在每次使用之前,都要清空字符串。记得用String::clear()
方法哦,这样可以轻松清空字符串实例。”
@@ -10,11 +10,12 @@ fn main() {
println!("The sum of two dice is: {sum_of_two_dice}");
let mut previous_guess: Option<u32> = None;
+ let mut guess = String::new();
loop {
println!("Please input your guess (between 2 and 12).");
- let mut guess = String::new();
+ guess.