Rust 基础教程

Rust 基础教程:从零开始的系统编程之旅

前言

Rust 是一门追求性能、可靠性和生产力的系统编程语言。自 2015 年发布 1.0 版本以来,连续多年在 Stack Overflow 开发者调查中被评为"最受喜爱的编程语言"。本教程面向有基本编程经验的读者,带你快速上手 Rust 核心概念。


目录

  1. 环境搭建
  2. [Hello World 与 Cargo](#Hello World 与 Cargo)
  3. 变量与可变性
  4. 数据类型
  5. 所有权与借用
  6. 结构体与枚举
  7. 错误处理
  8. 实战:命令行工具
  9. 总结

一、环境搭建

安装 Rust

推荐使用 rustup 工具链管理器,一条命令搞定:

Windows :下载并运行 rustup-init.exe

macOS / Linux

bash 复制代码
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装完成后验证:

bash 复制代码
rustc --version
cargo --version

推荐 IDE

  • VS Code + rust-analyzer 插件(最主流)
  • IntelliJ IDEA / CLion + Rust 插件
  • RustRover(JetBrains 官方 Rust IDE)

二、Hello World 与 Cargo

创建项目

bash 复制代码
cargo new hello_rust
cd hello_rust

生成的目录结构:

复制代码
hello_rust/
├── Cargo.toml      # 项目配置文件(类似 package.json)
└── src/
    └── main.rs     # 程序入口

src/main.rs 中的代码:

rust 复制代码
fn main() {
    println!("Hello, world!");
}

编译与运行

bash 复制代码
cargo build          # 编译(debug 模式)
cargo run            # 编译并运行
cargo build --release # 生产模式编译(优化)

Cargo.toml 基础

toml 复制代码
[package]
name = "hello_rust"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = "1.0"       # 添加第三方依赖

三、变量与可变性

Rust 变量默认不可变,这是安全性的基石之一。

rust 复制代码
fn main() {
    let x = 5;           // 不可变绑定
    // x = 6;            // ❌ 编译错误!cannot assign twice to immutable variable

    let mut y = 10;      // 可变绑定,使用 mut 关键字
    y = 20;              // ✅ 合法

    println!("x = {}, y = {}", x, y);
}

常量

rust 复制代码
const MAX_POINTS: u32 = 100_000;  // 必须标注类型,命名全大写

变量遮蔽(Shadowing)

rust 复制代码
fn main() {
    let x = 5;
    let x = x + 1;       // 用 let 重新声明,遮蔽旧值
    let x = x * 2;       // 可以改变类型
    println!("x = {}", x); // 12
}

四、数据类型

标量类型

类型 示例 说明
i8 / i16 / i32 / i64 / i128 let a: i32 = 42; 有符号整数
u8 / u16 / u32 / u64 / u128 let b: u8 = 255; 无符号整数
isize / usize let c: usize = 100; 平台相关整数
f32 / f64 let d: f64 = 3.14; 浮点数(默认 f64)
bool let e: bool = true; 布尔值
char let f: char = '🦀'; Unicode 字符(4字节)

复合类型

rust 复制代码
// 元组(Tuple)
let tup: (i32, f64, char) = (500, 6.4, 'R');
let (x, y, z) = tup;          // 解构
println!("{}, {}, {}", tup.0, tup.1, tup.2); // 索引访问

// 数组(固定长度,栈上分配)
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let zeros = [0; 10];         // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
println!("{}", arr[0]);

Vector(动态数组)

rust 复制代码
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);

let v2 = vec![1, 2, 3, 4];   // 宏创建

for i in &v2 {
    println!("{}", i);
}

五、所有权与借用

这是 Rust 最核心的特性------无需垃圾回收器即可保证内存安全

所有权规则

  1. Rust 中每个值都有一个所有者(owner)
  2. 同一时刻,一个值只能有一个所有者。
  3. 当所有者离开作用域,值被自动释放(调用 drop)。
rust 复制代码
fn main() {
    let s1 = String::from("hello");  // s1 拥有字符串
    let s2 = s1;                     // 所有权转移(move),s1 失效

    // println!("{}", s1);           // ❌ s1 已失效
    println!("{}", s2);              // ✅
}

克隆(深拷贝)

rust 复制代码
let s1 = String::from("hello");
let s2 = s1.clone();  // 深拷贝,s1 仍可用
println!("s1 = {}, s2 = {}", s1, s2);

引用与借用

rust 复制代码
fn main() {
    let s = String::from("hello");

    let len = calculate_length(&s);  // 不可变引用(借用)
    println!("'{}' 的长度是 {}", s, len); // s 仍可用
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

可变引用

rust 复制代码
fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s); // "hello, world"
}

fn change(s: &mut String) {
    s.push_str(", world");
}

引用的规则(防止数据竞争):

  • 同一时刻,要么一个可变引用 ,要么多个不可变引用,不能同时存在。
  • 引用必须始终有效(不能悬垂)。
rust 复制代码
let mut s = String::from("hello");
let r1 = &s;     // 不可变引用
let r2 = &s;     // 不可变引用
// let r3 = &mut s; // ❌ 不能同时存在可变引用
println!("{}, {}", r1, r2);

六、结构体与枚举

结构体

rust 复制代码
// 定义
struct User {
    username: String,
    email: String,
    active: bool,
}

// 实例化
let user1 = User {
    email: String::from("alice@example.com"),
    username: String::from("alice"),
    active: true,
};

// 字段更新语法
let user2 = User {
    email: String::from("bob@example.com"),
    ..user1  // 其余字段继承 user1
};

为结构体实现方法

rust 复制代码
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 关联函数(构造器)
    fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }

    // 方法(&self 不可变借用)
    fn area(&self) -> u32 {
        self.width * self.height
    }

    // 方法(&mut self 可变借用)
    fn set_width(&mut self, width: u32) {
        self.width = width;
    }
}

fn main() {
    let mut rect = Rectangle::new(30, 50);
    println!("面积: {}", rect.area()); // 1500
    rect.set_width(40);
    println!("新面积: {}", rect.area()); // 2000
}

枚举与模式匹配

rust 复制代码
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn process(msg: &Message) {
    match msg {
        Message::Quit => println!("退出"),
        Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
        Message::Write(text) => println!("消息: {}", text),
        Message::ChangeColor(r, g, b) => println!("颜色: ({}, {}, {})", r, g, b),
    }
}

Option 枚举

Rust 没有 null,使用 Option<T> 表达"可能有值":

rust 复制代码
fn find_user(id: u32) -> Option<String> {
    if id == 1 {
        Some(String::from("Alice"))
    } else {
        None
    }
}

fn main() {
    match find_user(1) {
        Some(name) => println!("找到: {}", name),
        None => println!("未找到用户"),
    }

    // if let 简写
    if let Some(name) = find_user(2) {
        println!("找到: {}", name);
    } else {
        println!("未找到用户");
    }
}

七、错误处理

Rust 将错误分为两类:可恢复错误Result)和不可恢复错误panic!)。

Result 类型

rust 复制代码
use std::fs::File;
use std::io::{self, Read};

fn read_file(path: &str) -> Result<String, io::Error> {
    let mut file = File::open(path)?;  // ? 运算符:出错则提前返回 Err
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

fn main() {
    match read_file("hello.txt") {
        Ok(content) => println!("文件内容:\n{}", content),
        Err(e) => eprintln!("读取失败: {}", e),
    }
}

panic! 宏

rust 复制代码
fn main() {
    // panic!("程序崩溃!"); // 不可恢复,立即终止

    let v = vec![1, 2, 3];
    // v[99]; // 索引越界,触发 panic
}

unwrap 与 expect

rust 复制代码
let f = File::open("hello.txt").unwrap();              // 出错则 panic
let f = File::open("hello.txt").expect("无法打开文件"); // 带自定义消息的 panic

自定义错误类型

rust 复制代码
#[derive(Debug)]
enum AppError {
    Io(std::io::Error),
    Parse(std::num::ParseIntError),
}

impl From<std::io::Error> for AppError {
    fn from(e: std::io::Error) -> Self { AppError::Io(e) }
}

impl From<std::num::ParseIntError> for AppError {
    fn from(e: std::num::ParseIntError) -> Self { AppError::Parse(e) }
}

八、实战:命令行工具

写一个统计文件中词频的小工具,综合运用前面学到的知识。

创建项目

bash 复制代码
cargo new wordcount
cd wordcount

编辑 Cargo.toml

toml 复制代码
[package]
name = "wordcount"
version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "4", features = ["derive"] }

完整代码 (src/main.rs)

rust 复制代码
use clap::Parser;
use std::collections::HashMap;
use std::fs;

/// 统计文本文件中单词的出现频率
#[derive(Parser, Debug)]
#[command(version, about)]
struct Args {
    /// 输入文件路径
    #[arg(short, long)]
    input: String,

    /// 显示前 N 个高频词
    #[arg(short, long, default_value_t = 10)]
    top: usize,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();

    let content = fs::read_to_string(&args.input)?;

    let mut word_count: HashMap<String, usize> = HashMap::new();

    for word in content
        .to_lowercase()
        .split(|c: char| !c.is_alphanumeric())
        .filter(|w| !w.is_empty())
    {
        *word_count.entry(word.to_string()).or_insert(0) += 1;
    }

    let mut sorted: Vec<(&String, &usize)> = word_count.iter().collect();
    sorted.sort_by(|a, b| b.1.cmp(a.1));

    println!("=== 词频统计 Top {} ===\n", args.top);
    println!("{:<20} {:>8}", "单词", "次数");
    println!("{}", "-".repeat(30));

    for (word, count) in sorted.iter().take(args.top) {
        println!("{:<20} {:>8}", word, count);
    }

    Ok(())
}

运行

bash 复制代码
cargo run -- --input sample.txt --top 10

这个例子涵盖了:结构体派生宏、错误处理 (?)、HashMap、迭代器、泛型、trait 对象等多个核心概念。


九、总结

概念 要点
所有权 每值一主,离开作用域自动释放
借用 引用不转移所有权,可变借用互斥
生命周期 编译器静态保证引用有效性
枚举 + match 表达力极强,模式匹配穷尽检查
Result / Option 无 null、无异常,编译时强制处理错误
Trait 类似接口的共享行为抽象
零成本抽象 高级语法编译后与手写底层代码性能相当