跳转到内容

Rust 类型转换

Rust 不会自动帮你把一种类型转换成另一种:如果在同一个表达式里混用 i32 和 f64,代码就编译不过。你必须显式地转换。这是有意为之的:在其他语言里,悄无声息的类型转换是 bug 的常见来源。

用 as 关键字把一种数字类型转换(cast)成另一种:

fn main() {
let x: i32 = 10;
let y: f64 = x as f64; // i32 -> f64
println!("{y}");
}
▶ 动手试一试

这件事比看上去更重要。试试不做转换直接让两个整数相除,你得到的是整数除法,而不是你想要的小数结果:

fn main() {
let a = 10;
let b = 3;
println!("{}", a / b); // 3(整数除法)
println!("{}", a as f64 / b as f64); // 3.3333333333333335
}
▶ 动手试一试

把大类型转换成小类型(比如 i32 转 u8)不会报错,它只会截断,结果可能会吓你一跳:

fn main() {
let big: i32 = 300;
let small = big as u8; // u8 只能存 0-255
println!("{small}"); // 44,不是 300:它绕回去了
}
▶ 动手试一试

as 不只适用于整数,还能处理另外几种类型。每一种都有一条值得记住的规则:

fn main() {
println!("{}", 3.99_f64 as i32); // 3:小数部分直接被砍掉
println!("{}", -3.99_f64 as i32); // -3:向零截断,而不是向下取整
println!("{}", 3.99_f64.round() as i32); // 4:想四舍五入就先调用 round
println!("{}", 300.0_f64 as u8); // 255:太大,停在最大值
println!("{}", -5.0_f64 as u8); // 0:太小,停在最小值
println!("{}", 'A' as u32); // 65:char 对应的编号
println!("{}", 97_u8 as char); // a:再转回来
println!("{}", true as i32); // 1(false 是 0)
}

输出:

3
-3
4
255
0
65
a
1
▶ 动手试一试

要点如下:

  • 浮点数转整数会扔掉小数点后的所有内容,不会四舍五入。想得到最接近的整数,先调用 .round()。
  • 超出范围的浮点数会被限制(clamp)到能放下的最接近的值(300.0 转成 u8 变成 255)。这和整数不同,整数会绕回(上面的 300 变成了 44)。
  • 字符背后都有一个数字(它的 Unicode 码位)。'A' as u32 得到 65。只有 u8 能直接转换回 char;更大的数字要用 char::from_u32,因为不是每个数字都对应一个有效字符。
  • 布尔值会变成 1 或 0。反过来不行:1 as bool 无法编译,要改用 n != 0 这样的比较。

as 只能在数字类型之间转换。要把文本(比如用户输入的内容)变成数字,用 .parse():

fn main() {
let text = "42";
let number: i32 = text.parse().expect("不是有效的数字");
println!("{}", number + 8);
}

输出:

50
▶ 动手试一试

.parse() 可能会失败(万一文本根本不是数字呢?),所以它返回的是一个 Result,而不是直接返回数字。我们还没讲到 Result,目前可以把 .expect("提示信息") 理解为“如果失败,就带着这条信息崩溃”。正确的处理方式会在 Result 一课里讲。

反方向要简单得多:几乎所有类型都能用 .to_string() 变成 String:

fn main() {
let number = 42;
let text = number.to_string();
println!("{}", text + "!");
}
▶ 动手试一试

用 i32 存三次考试的分数:7、8 和 10。打印它们的总和,再打印保留两位小数的平均分。提示:两个整数相除得到的是 8 而不是 8.33,所以要先把总和转换成 f64。{:.2} 会打印两位小数。

查看答案
fn main() {
let a: i32 = 7;
let b: i32 = 8;
let c: i32 = 10;
let sum = a + b + c;
let average = sum as f64 / 3.0; // 25.0 / 3.0,而不是 25 / 3
println!("总分:{sum}");
println!("平均分:{:.2}", average);
}

输出:

总分:25
平均分:8.33
▶ 动手试一试

商店给你的价格是文本 "4.50",数量是 i32 类型的 3。把文本转成 f64,乘以数量,打印出 总价:13.5。提示:这一课的两个工具都要用上:文本用 .parse(),数量用 as f64。

查看答案
fn main() {
let price_text = "4.50";
let quantity: i32 = 3;
let price: f64 = price_text.parse().expect("不是有效的价格");
let total = price * quantity as f64; // f64 * f64
println!("总价:{total}");
}

输出:

总价:13.5
▶ 动手试一试

如果没有 as f64,Rust 会拒绝把 f64 和 i32 相乘,因为它从不替你混用数字类型。

  • Rust 从不自动转换类型,你总是要显式转换。
  • 用 as 在数字类型之间转换:x as f64、x as u8。
  • 向下转换成更小的类型会悄悄截断,不会报错,所以要小心。
  • 浮点数转整数会砍掉小数部分(想四舍五入就先用 .round())。char 和 bool 也能转换成数字。
  • 用 .parse() 把文本变成数字,用 .to_string() 反过来转换。

小测验

1. 在 Rust 里不做转换,直接计算 10 / 3 这样的两个整数相除,会怎样?

2. 把 300_i32 用 as 转换成 u8,会发生什么?

3. 因为转换可能失败,用 .parse() 把字符串转成数字时,它返回什么?

得分: 0 / 3