Rust 入门系列(第 2 篇,共 5 篇) 前置阅读:第 1 篇 - Rust 是什么?能做什么? 阅读时间:25 分钟
引言
这篇文章会快速过一遍 Rust 的核心语法,包括环境搭建、基础数据类型、函数、控制流、Struct/Enum 等。目标是让你能写出第一个 Rust 程序,理解基本概念。
一、环境搭建
1.1 安装 Rust
bash
1234
# macOS/Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Windows:访问 https://rustup.rs/ 下载安装器安装后得到:
-
rustc:Rust 编译器 -
cargo:包管理器 + 构建工具 -
rustup:工具链管理器
验证安装:
bash
12
rustc --version
cargo --version1.2 创建第一个项目
bash
12
cargo new hello_rust
cd hello_rust目录结构:
plaintext
12345
hello_rust/
├── Cargo.toml # 项目配置(类似 package.json)
├── src/
│ └── main.rs # 源代码
└── target/ # 构建输出1.3 运行项目
bash
12
cargo run
# 输出:Hello, world!常用命令:
bash
1234567
cargo build # 编译(debug 模式)
cargo build --release # 编译(release 模式,优化)
cargo run # 编译 + 运行
cargo test # 运行测试
cargo check # 快速检查代码(不生成可执行文件)
cargo fmt # 格式化代码
cargo clippy # 代码检查二、变量与可变性
2.1 默认不可变
Rust 变量默认不可变:
rust
12345
let x = 5;
// x = 6; // ❌ 错误:不能修改不可变变量
let mut y = 5; // 需要 mut 关键字
y = 6; // ✅ OK为什么默认不可变?
- 更安全:避免意外修改
- 更容易并发:不可变数据无需加锁
2.2 变量遮蔽(Shadowing)
可以用 let 重新声明同名变量:
rust
1234
let x = 5;
let x = x + 1; // 遮蔽前一个 x
let x = x * 2; // 再次遮蔽
println!("{}", x); // 12遮蔽 vs mut:
rust
1234567
// 遮蔽:可以改变类型
let spaces = " ";
let spaces = spaces.len(); // ✅ 字符串 → 数字
// mut:不能改变类型
let mut count = " ";
// count = count.len(); // ❌ 错误:类型不匹配三、数据类型
3.1 标量类型
整数
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| arch | isize | usize |
rust
12345
let decimal = 98_222; // 十进制
let hex = 0xff; // 十六进制
let octal = 0o77; // 八进制
let binary = 0b1111_0000; // 二进制
let byte = b'A'; // 字节(u8)默认类型:i32
浮点数
rust
12
let x = 2.0; // f64(默认)
let y: f32 = 3.0; // f32布尔值
rust
12
let t = true;
let f: bool = false;字符
rust
12
let c = 'z';
let emoji = '😻'; // Unicode 字符3.2 复合类型
元组
rust
12345678
let tup: (i32, f64, u8) = (500, 6.4, 1);
// 解构
let (x, y, z) = tup;
// 索引访问
let five_hundred = tup.0;
let six_point_four = tup.1;数组
rust
12345678
let arr = [1, 2, 3, 4, 5]; // 类型:[i32; 5]
let arr: [i32; 5] = [1, 2, 3, 4, 5]; // 显式标注
let arr = [3; 5]; // [3, 3, 3, 3, 3]
// 访问
let first = arr[0];
let second = arr[1];数组 vs Vec:
- 数组:固定长度,存储在栈上
- Vec:可变长度,存储在堆上
rust
1
let v: Vec<i32> = vec![1, 2, 3];3.3 字符串
Rust 有两种字符串:
&str(字符串切片)
rust
1
let s: &str = "Hello, world!"; // 不可变,存储在栈String(堆字符串)
rust
123
let mut s = String::from("Hello");
s.push_str(", world!"); // 可变
println!("{}", s); // "Hello, world!"转换:
rust
123
let s1: &str = "Hello";
let s2: String = s1.to_string();
let s3: &str = &s2; // String → &str四、函数
4.1 基本语法
rust
1234567
fn add(x: i32, y: i32) -> i32 {
x + y // 表达式,自动返回(无分号)
}
fn add2(x: i32, y: i32) -> i32 {
return x + y; // 也可以用 return(需要分号)
}4.2 无返回值
rust
12345678
fn greet(name: &str) {
println!("Hello, {}!", name);
}
// 等价于
fn greet2(name: &str) -> () {
println!("Hello, {}!", name);
}4.3 语句 vs 表达式
语句:不返回值,以分号结尾
rust
1
let x = 5; // 语句表达式:返回值,无分号
rust
1234
let y = {
let x = 3;
x + 1 // 表达式,返回 4
};五、控制流
5.1 if 表达式
rust
12345678910
let number = 6;
if number % 2 == 0 {
println!("偶数");
} else {
println!("奇数");
}
// if 是表达式
let result = if number < 5 { "small" } else { "big" };5.2 循环
loop
rust
12345678
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // 返回 20
}
};while
rust
123456
let mut number = 3;
while number != 0 {
println!("{}", number);
number -= 1;
}for
rust
123456789101112131415
// 遍历范围
for i in 0..5 {
println!("{}", i); // 0, 1, 2, 3, 4
}
// 遍历数组
let arr = [10, 20, 30];
for element in arr.iter() {
println!("{}", element);
}
// 带索引
for (index, value) in arr.iter().enumerate() {
println!("{}: {}", index, value);
}六、模式匹配
6.1 match 表达式
rust
12345678
let number = 13;
match number {
1 => println!("One!"),
2 | 3 | 5 | 7 | 11 => println!("Prime"),
13..=19 => println!("A teen"),
_ => println!("Other"),
}6.2 匹配 Option
rust
1234567
let some_number = Some(5);
let no_number: Option<i32> = None;
match some_number {
Some(i) => println!("Got: {}", i),
None => println!("No value"),
}6.3 if let 简化
rust
123456789101112
let some_value = Some(3);
// match 写法
match some_value {
Some(3) => println!("three"),
_ => (),
}
// if let 简化
if let Some(3) = some_value {
println!("three");
}七、Struct(结构体)
7.1 定义和使用
rust
123456789101112131415
struct User {
username: String,
email: String,
age: u32,
active: bool,
}
let user1 = User {
username: String::from("alice"),
email: String::from("[email protected]"),
age: 25,
active: true,
};
println!("{}", user1.username);7.2 更新语法
rust
1234
let user2 = User {
email: String::from("[email protected]"),
..user1 // 其余字段从 user1 复制
};7.3 方法
rust
1234567891011121314
impl User {
fn greet(&self) {
println!("Hi, I'm {}!", self.username);
}
fn is_adult(&self) -> bool {
self.age >= 18
}
}
user1.greet();
if user1.is_adult() {
println!("Adult");
}7.4 关联函数
rust
12345678910111213141516
impl User {
fn new(username: String, email: String, age: u32) -> User {
User {
username,
email,
age,
active: true,
}
}
}
let user = User::new(
String::from("alice"),
String::from("[email protected]"),
25
);八、Enum(枚举)
8.1 基本用法
rust
1234567
enum IpAddr {
V4,
V6,
}
let ipv4 = IpAddr::V4;
let ipv6 = IpAddr::V6;8.2 携带数据
rust
1234567
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));8.3 复杂 Enum
rust
12345678910111213141516171819202122
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(text) => println!("Text: {}", text),
Message::ChangeColor(r, g, b) => {
println!("Color: ({}, {}, {})", r, g, b)
}
}
}
}
let msg = Message::Write(String::from("Hello"));
msg.call();九、Option 和 Result
9.1 Option:处理空值
Rust 没有 null,用 Option<T> 表示”可能有值”:
rust
1234
enum Option<T> {
Some(T),
None,
}示例:
rust
123456789101112
fn divide(a: i32, b: i32) -> Option<i32> {
if b == 0 {
None
} else {
Some(a / b)
}
}
match divide(10, 2) {
Some(result) => println!("结果: {}", result),
None => println!("无法除以 0"),
}常用方法:
rust
12345678910
let x = Some(5);
// unwrap:有值则返回,无值则 panic
let y = x.unwrap();
// unwrap_or:无值时返回默认值
let z = x.unwrap_or(0);
// map:转换内部值
let doubled = x.map(|n| n * 2); // Some(10)9.2 Result:错误处理
rust
1234
enum Result<T, E> {
Ok(T),
Err(E),
}示例:
rust
12345678910
use std::fs::File;
fn open_file() -> Result<File, std::io::Error> {
File::open("hello.txt")
}
match open_file() {
Ok(file) => println!("文件打开成功"),
Err(error) => println!("打开失败: {:?}", error),
}? 操作符:
rust
123456
fn read_username() -> Result<String, std::io::Error> {
let mut file = File::open("username.txt")?; // 失败则提前返回 Err
let mut username = String::new();
file.read_to_string(&mut username)?;
Ok(username)
}十、实战:完整小程序
10.1 猜数字游戏
rust
1234567891011121314151617181920212223242526272829303132
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("猜数字游戏!");
let secret_number = rand::thread_rng().gen_range(1..=100);
loop {
println!("请输入你的猜测:");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("读取失败");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
match guess.cmp(&secret_number) {
Ordering::Less => println!("太小了!"),
Ordering::Greater => println!("太大了!"),
Ordering::Equal => {
println!("猜对了!");
break;
}
}
}
}Cargo.toml:
toml
12
[dependencies]
rand = "0.8"运行:
bash
1
cargo run十一、核心概念总结
11.1 关键要点
- 默认不可变:
let x不可变,let mut x可变 - 无 null:用
Option<T>替代 - 无异常:用
Result<T, E>处理错误 - 类型严格:不会自动转换类型
- 表达式优先:
if、match、loop都可以返回值
11.2 常见困惑
| 问题 | 解答 |
|---|---|
| 为什么没分号? | 表达式返回值不需要分号 |
| &str vs String? | &str 不可变切片,String 可变堆字符串 |
| Option 有什么用? | 替代 null,强制处理空值情况 |
| match 必须穷尽? | 是的,编译器会检查 |
十二、下一步
12.1 练习建议
- 修改猜数字游戏:加入重试次数限制
- 待办事项 CLI:用 Vec 存储任务
- 简单计算器:实现加减乘除
12.2 推荐资源
- Rustlings:交互式练习
- Rust by Example:代码示例
- The Book:官方教程
系列预告
第 3 篇:《Rust 入门(三):所有权、借用、生命周期》
- 所有权系统详解
- 借用和引用规则
- 生命周期标注
第 4 篇:《Rust 入门(四):实战 CLI 工具》 第 5 篇:《Rust 入门(五):Rust + WebAssembly》
参考资料
- The Rust Programming Language Book
- Cargo Book
- Rust for JavaScript Developers
- Rust Enums and Pattern Matching
- Rust Option and Result