Skip to content

Rust - 数据类型

一个类型系统定义了编程语言支持的不同类别的值。Rust 是一种静态类型语言,这意味着它必须在编译时知道所有变量的类型。这项强大的特性允许编译器验证代码的正确性,预防大量的错误,并为自动补全和重构等工具提供出色的支持。

虽然你必须在函数签名和结构体中明确指定类型,但编译器通常可以从变量的初始值推断出其类型。这让你能够同时享受到两方面的好处:强大的类型安全,同时又避免了不必要的冗余。

在 Rust 中,你使用 let 关键字声明变量。默认情况下,变量是不可变的,这是 Rust 的一个关键特性,它有助于编写更安全、更可预测的代码。要创建一个可变变量,你需要使用 let mut 关键字。

fn main() {
// 类型由编译器推断
let company = "TechCorp"; // &str (string slice)
let rating = 4.5; // f64 (floating-point number)
let is_profitable = true; // bool (boolean)
let logo = '🦀'; // char (Unicode character)
// 你也可以显式地标注类型
let mut project_count: u32 = 10; // u32 (unsigned 32-bit integer)
project_count += 1;
println!("Company: {}", company);
println!("Rating: {}", rating);
println!("Is Profitable? {}", is_profitable);
println!("Logo: {}", logo);
println!("Current Projects: {}", project_count);
}

在上面的示例中,company、rating、is_profitable 和 logo 是不可变的。project_count 是可变的,并且其 u32 类型被显式声明。println! 宏使用了 {} 占位符,它们在执行时会被变量的值替换。

你可以在终端中运行 cargo check 来检查代码的正确性而无需编译。要编译并运行,请使用 cargo run。

代码的输出如下:

Company: TechCorp
Rating: 4.5
Is Profitable? true
Logo: 🦀
Current Projects: 11

标量类型代表一个单一的值。Rust 有四种主要的标量类型:

  • 整数(例如,5、-10)
  • 浮点数(例如,3.14、99.9)
  • 布尔值(例如,true、false)
  • 字符(例如,'a'、'%'、'π')

整数是没有小数部分的数字。它们可以是有符号的(可以为负、零或正)或无符号的(只能为零或正)。以下是内置的整数类型:

大小有符号无符号
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
Archisizeusize

isize 和 usize 类型取决于你的程序运行的计算机架构:在 32 位架构上是 32 位,在 64 位架构上是 64 位。它们主要用于集合的索引。

最佳实践:默认的整数类型是 i32,这通常是兼顾速度和兼容性的好选择。在对集合进行索引时使用 usize。仅当你存在特定需求时(例如节省内存或与具有固定大小类型的外部系统进行接口交互时),才使用其他整数类型。

整数溢出发生在你尝试将一个超出类型有效范围的值赋给变量时(例如,将 256 赋值给只能保存 0 到 255 的 u8 类型)。Rust 的行为取决于构建配置文件:

  • 调试模式:Rust 会检查整数溢出。如果发生溢出,程序将崩溃并立即退出。这有助于你在开发过程中发现错误。
  • 发布模式:Rust 不会检查溢出。相反,它会执行二进制补码环绕。例如,u8 中的 256 会变成 0,257 会变成 1,以此类推。

如果你需要显式地处理溢出,标准库提供了以下方法:

fn main() {
let a: u8 = 255;
// checked_* 方法返回一个 Option<T>
let checked_add = a.checked_add(1); // 返回 None,因为它会溢出
let checked_add_ok = a.checked_add(0); // 返回 Some(255)
// wrapping_* 方法执行二进制补码环绕
let wrapping_add = a.wrapping_add(1); // 返回 0
// saturating_* 方法返回该类型的最大或最小值
let saturating_add = a.saturating_add(1); // 返回 255
println!("Checked add (255 + 1): {:?}", checked_add);
println!("Checked add (255 + 0): {:?}", checked_add_ok);
println!("Wrapping add (255 + 1): {}", wrapping_add);
println!("Saturating add (255 + 1): {}", saturating_add);
}

依赖隐式环绕可能导致意想不到的错误。最佳实践是使用 checked_* 方法并在你预期操作可能溢出时处理 None 情况。

Rust 有两种浮点类型:f32(单精度)和 f64(双精度)。默认类型是 f64,因为在现代 CPU 上,它的速度与 f32 大致相同,但能够提供更高的精度。

fn main() {
let pi = 3.14159; // 默认为 f64
let interest_rate: f32 = 0.05; // 显式声明为 f32
println!("Pi is approximately {}", pi);
println!("Interest Rate is {}", interest_rate);
// 为了提高可读性,你可以在数值字面量中使用下划线
let large_number = 1_000_000.555_001;
println!("A large number: {}", large_number);
}

重要提示:Rust 不会对数字类型之间执行隐式类型转换。以下代码将无法编译:

// 这将无法编译!
fn main() {
let my_integer: i32 = 10;
// error[E0308]: mismatched types
// let my_float: f64 = my_integer;
}
// 要修复它,你必须使用 `as` 关键字进行显式转换:
fn fixed() {
let my_integer: i32 = 10;
let my_float: f64 = my_integer as f64; // 这是正确的
println!("Float value: {}", my_float);
}

布尔类型 bool 有两个可能的值:true 和 false。它最常用于像 if 表达式这样的条件逻辑中。

字符类型 char 是 Rust 最原始的字母类型。它使用单引号指定。重要的是,char 代表一个 Unicode 标量值,这意味着它能表示的远不止 ASCII 字符。一个 char 占用 4 字节大小,可以表示字母、数字、符号,甚至表情符号。

fn main() {
let is_learning_rust: bool = true;
println!("Are we learning Rust? {}", is_learning_rust);
let letter_a = 'a';
let checkmark = '✅';
println!("A simple letter: {}", letter_a);
println!("A checkmark emoji: {}", checkmark);
}