Rust编程语言从入门到实战完全教程

教程简介

全面讲解Rust系统编程语言的核心特性与实战开发,涵盖所有权与借用机制、生命周期、模式匹配、错误处理、trait与泛型、并发编程、Cargo包管理、Web开发、CLI工具开发、FFI与跨语言调用等核心内容。

Rust 编程语言从入门到实战完全教程

关键词:Rust、系统编程、高性能、并发、编程语言

适用读者:有一定编程基础(熟悉 C/C++/Python/Go 等任意语言),希望系统学习 Rust 的开发者。


目录

  1. 为什么选择 Rust
  2. 环境搭建与第一个程序
  3. Cargo 包管理与项目结构
  4. 核心基础:变量、类型与控制流
  5. 所有权与借用机制
  6. 生命周期(Lifetime)
  7. 模式匹配与枚举
  8. 错误处理:Result 与 Option
  9. Trait 与泛型
  10. 并发编程与 async/await
  11. 常用 Crate 介绍
  12. Web 开发实战:Axum 与 Actix-web
  13. CLI 工具开发
  14. FFI 与跨语言调用
  15. 学习资源与进阶路径

1. 为什么选择 Rust

Rust 是由 Mozilla 发起、目前由 Rust Foundation 维护的系统级编程语言。它的核心设计目标是 "零成本抽象""内存安全无需垃圾回收"

1.1 Rust 的三大卖点

  • 内存安全:编译期通过所有权系统杜绝空指针、悬垂引用、数据竞争等内存 bug,无需 GC。
  • 高性能:与 C/C++ 同一量级的运行速度,适合系统编程、游戏引擎、嵌入式等场景。
  • 开发体验:优秀的编译器错误提示、内置测试框架、Cargo 包管理器,让开发效率极高。

1.2 谁在用 Rust

  • Mozilla:Firefox 浏览器引擎 Servo
  • Amazon:Firecracker 虚拟化引擎
  • Microsoft:Windows 内核部分模块
  • Google:Android 操作系统底层组件
  • Discord:从 Go 迁移到 Rust 后性能提升显著
  • 字节跳动、蚂蚁集团:大量微服务和基础设施

2. 环境搭建与第一个程序

2.1 安装 Rust

推荐通过 rustup 安装:

# Linux / macOS
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 安装完成后重启终端,或执行:
source $HOME/.cargo/env

# 验证安装
rustc --version
cargo --version

Windows 用户可下载 rustup-init.exe 直接安装。

2.2 Hello World

创建文件 main.rs

fn main() {
    println!("Hello, world!");
}

编译并运行:

rustc main.rs
./main

但实际开发中我们几乎不直接使用 rustc,而是通过 Cargo 管理项目。


3. Cargo 包管理与项目结构

3.1 创建项目

cargo new hello_rust
cd hello_rust

生成的目录结构:

hello_rust/
├── Cargo.toml        # 项目元数据与依赖声明
├── src/
│   └── main.rs       # 入口文件
└── .gitignore

3.2 Cargo.toml 详解

[package]
name = "hello_rust"
version = "0.1.0"
edition = "2021"          # Rust 版本:2015 / 2018 / 2021 / 2024
authors = ["Your Name <you@example.com>"]
description = "A demo project"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }

3.3 常用 Cargo 命令

cargo build             # 编译(debug 模式)
cargo build --release   # 编译(release 模式,优化级别更高)
cargo run               # 编译并运行
cargo test              # 运行测试
cargo doc --open        # 生成并打开文档
cargo clippy            # Lint 检查
cargo fmt               # 代码格式化
cargo add serde         # 添加依赖(Cargo 1.62+)

4. 核心基础:变量、类型与控制流

4.1 变量与可变性

Rust 中变量默认 不可变,这是其安全哲学的体现:

fn main() {
    let x = 5;
    // x = 6;  // 编译错误!x 不可变

    let mut y = 10;
    y = 20;     // 合法,mut 声明了可变性

    // Shadowing(遮蔽):重新 let 同名变量,可改变类型
    let spaces = "   ";
    let spaces = spaces.len();  // 从 &str 变为 usize
}

4.2 基本类型

类别 类型 示例
整数 i8, i16, i32, i64, i128, isize let a: i32 = 42;
无符号整数 u8, u16, u32, u64, u128, usize let b: u64 = 100;
浮点数 f32, f64 let pi: f64 = 3.14159;
布尔 bool let flag = true;
字符 char(4 字节 Unicode) let c = '🦀';
元组 (T1, T2, ...) let tup: (i32, f64) = (5, 3.14);
数组 [T; N] let arr = [1, 2, 3];

4.3 函数

fn add(a: i32, b: i32) -> i32 {
    a + b   // 没有分号 = 返回值(表达式)
}

// 表达式 vs 语句
fn example() -> i32 {
    let x = {
        let y = 3;
        y + 1       // 表达式,作为块的返回值
    };               // x = 4
    x * 2            // 返回 8
}

4.4 控制流

fn main() {
    let number = 7;

    // if 表达式(可以赋值)
    let desc = if number > 0 { "positive" } else { "non-positive" };

    // loop 可以返回值
    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2;  // 返回 20
        }
    };

    // for 循环
    for i in 0..5 {
        println!("{}", i);
    }

    // 遍历迭代器
    let names = vec!["Alice", "Bob", "Charlie"];
    for name in names.iter() {
        println!("Hello, {}!", name);
    }
}

5. 所有权与借用机制

这是 Rust 最核心、最具特色的设计,也是新手最大的学习曲线。

5.1 所有权规则

三条铁律:

  1. 每个值都有且仅有一个所有者
  2. 同一时刻只能有一个所有者
  3. 所有者离开作用域时,值被自动释放(Drop)
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;           // s1 的所有权转移给 s2(Move)
    // println!("{}", s1);  // 编译错误!s1 已失效

    let s3 = s2.clone();   // 深拷贝,s2 和 s3 各自独立
    println!("{} {}", s2, s3);  // 合法
}

5.2 引用与借用

为了避免所有权转移带来的不便,Rust 提供 引用(Reference) 机制:

fn main() {
    let s = String::from("hello");

    // 不可变引用:可以有多个
    let r1 = &s;
    let r2 = &s;
    println!("{} {}", r1, r2);

    // 可变引用:同一时刻只能有一个
    let mut s2 = String::from("world");
    change(&mut s2);
    println!("{}", s2);  // "world!"
}

fn change(s: &mut String) {
    s.push('!');
}

借用规则

  • 可以有 任意数量 的不可变引用 &T
  • 恰好一个 可变引用 &mut T
  • 两者不能同时存在

这条规则在编译期就消除了数据竞争。

5.3 切片(Slice)

切片是对集合中部分元素的引用:

fn main() {
    let s = String::from("hello world");
    let hello = &s[0..5];    // "hello"
    let world = &s[6..11];   // "world"

    // 字符串字面量就是切片
    let literal: &str = "I am a slice";

    // 数组切片
    let arr = [1, 2, 3, 4, 5];
    let slice = &arr[1..3];  // [2, 3]
}

6. 生命周期(Lifetime)

6.1 为什么需要生命周期

当函数返回引用时,编译器需要确保返回的引用不会指向已被释放的数据:

// 错误示例:编译器无法推断生命周期
// fn longest(x: &str, y: &str) -> &str {
//     if x.len() > y.len() { x } else { y }
// }

// 正确:显式标注生命周期
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let s1 = String::from("long string");
    let result;
    {
        let s2 = String::from("xyz");
        result = longest(s1.as_str(), s2.as_str());
        println!("Longest: {}", result);  // 合法:s2 仍存活
    }
    // println!("{}", result);  // 编译错误!s2 已被释放
}

6.2 结构体中的生命周期

当结构体持有引用时,必须标注生命周期:

struct Excerpt<'a> {
    text: &'a str,
}

impl<'a> Excerpt<'a> {
    fn level(&self) -> i32 {
        3
    }

    fn announce_and_return(&self, announcement: &str) -> &str {
        println!("Attention: {}", announcement);
        self.text  // 返回的生命周期与 self 绑定
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence: &str = novel.split('.').next().unwrap();
    let excerpt = Excerpt { text: first_sentence };
    println!("{}", excerpt.text);
}

6.3 生命周期省略规则

编译器有三条推断规则,大多数情况无需手动标注:

  1. 每个引用参数获得各自的生命周期
  2. 如果只有一个输入生命周期,它被赋给所有输出引用
  3. 如果是方法(&self),self 的生命周期被赋给所有输出引用

7. 模式匹配与枚举

7.1 枚举定义

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    Color(i32, i32, i32),
}

7.2 match 表达式

fn describe(msg: &Message) -> String {
    match msg {
        Message::Quit => String::from("Quit"),
        Message::Move { x, y } => format!("Move to ({}, {})", x, y),
        Message::Write(text) => format!("Write: {}", text),
        Message::Color(r, g, b) => format!("Color #{:02x}{:02x}{:02x}", r, g, b),
    }
}

// match 必须穷尽所有模式,_ 是通配符
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

7.3 if let 简写

当你只关心一种情况时:

fn main() {
    let config_max = Some(3u8);

    // 完整 match
    match config_max {
        Some(max) => println!("Maximum: {}", max),
        _ => (),
    }

    // if let 简写
    if let Some(max) = config_max {
        println!("Maximum: {}", max);
    }

    // while let
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() {
        println!("{}", top);
    }
}

8. 错误处理:Result 与 Option

8.1 不可恢复错误:panic!

fn main() {
    // panic!("crash and burn");  // 程序直接终止

    // 数组越界也会 panic
    let v = vec![1, 2, 3];
    // v[99];  // panic: index out of bounds
}

8.2 可恢复错误:Result

use std::fs::File;
use std::io::{self, Read};

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

// 更优雅的链式写法
fn read_username_short() -> Result<String, io::Error> {
    let mut username = String::new();
    File::open("username.txt")?.read_to_string(&mut username)?;
    Ok(username)
}

// 最简写法
fn read_username_shortest() -> Result<String, io::Error> {
    std::fs::read_to_string("username.txt")
}

8.3 自定义错误类型

use std::fmt;
use std::num::ParseIntError;

#[derive(Debug)]
enum AppError {
    Parse(ParseIntError),
    Io(std::io::Error),
    Custom(String),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AppError::Parse(e) => write!(f, "Parse error: {}", e),
            AppError::Io(e) => write!(f, "IO error: {}", e),
            AppError::Custom(msg) => write!(f, "Custom error: {}", msg),
        }
    }
}

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

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

fn process() -> Result<i32, AppError> {
    let content = std::fs::read_to_string("number.txt")?;  // 自动转换
    let num: i32 = content.trim().parse()?;                  // 自动转换
    Ok(num * 2)
}

在实际项目中,推荐使用 thiserroranyhow crate 简化错误处理:

[dependencies]
thiserror = "1"
anyhow = "1"

9. Trait 与泛型

9.1 定义 Trait

trait Summary {
    // 必须实现的方法
    fn summarize(&self) -> String;

    // 默认实现
    fn preview(&self) -> String {
        format!("{}...", &self.summarize()[..20])
    }
}

struct Article {
    title: String,
    content: String,
}

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}: {}", self.title, &self.content[..50])
    }
}

9.2 泛型函数

// 泛型 + Trait Bound
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in &list[1..] {
        if item > largest {
            largest = item;
        }
    }
    largest
}

// where 子句(复杂约束时更清晰)
fn process<T, U>(t: T, u: U) -> String
where
    T: std::fmt::Display + Clone,
    U: std::fmt::Debug + Summary,
{
    format!("{} - {:?}", t, u.summarize())
}

9.3 泛型结构体与枚举

struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

// 仅为特定类型实现方法
impl Point<f64> {
    fn distance_from_origin(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

// Option 和 Result 就是泛型枚举
// enum Option<T> { Some(T), None }
// enum Result<T, E> { Ok(T), Err(E) }

9.4 Trait 对象(动态分发)

trait Drawable {
    fn draw(&self);
}

struct Circle { radius: f64 }
struct Square { side: f64 }

impl Drawable for Circle {
    fn draw(&self) { println!("Drawing circle r={}", self.radius); }
}
impl Drawable for Square {
    fn draw(&self) { println!("Drawing square s={}", self.side); }
}

// dyn Trait 实现动态多态
fn draw_all(shapes: &[Box<dyn Drawable>]) {
    for shape in shapes {
        shape.draw();
    }
}

fn main() {
    let shapes: Vec<Box<dyn Drawable>> = vec![
        Box::new(Circle { radius: 5.0 }),
        Box::new(Square { side: 3.0 }),
    ];
    draw_all(&shapes);
}

10. 并发编程与 async/await

10.1 线程基础

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..=5 {
            println!("Spawned thread: {}", i);
            thread::sleep(Duration::from_millis(100));
        }
    });

    for i in 1..=3 {
        println!("Main thread: {}", i);
        thread::sleep(Duration::from_millis(150));
    }

    handle.join().unwrap();  // 等待子线程完成
}

10.2 消息传递(Channel)

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let messages = vec!["hello", "from", "the", "other", "side"];
        for msg in messages {
            tx.send(msg).unwrap();
            thread::sleep(Duration::from_millis(200));
        }
    });

    for received in rx {
        println!("Got: {}", received);
    }
}

10.3 共享状态:Mutex 与 Arc

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());  // 10
}

10.4 async/await 异步编程

use tokio::time::{sleep, Duration};

async fn fetch_data(id: u32) -> String {
    sleep(Duration::from_millis(100)).await;
    format!("Data from task {}", id)
}

#[tokio::main]
async fn main() {
    // 并发执行多个异步任务
    let (r1, r2, r3) = tokio::join!(
        fetch_data(1),
        fetch_data(2),
        fetch_data(3),
    );
    println!("{}\n{}\n{}", r1, r2, r3);

    // 异步任务生成
    let handle = tokio::spawn(async {
        fetch_data(4).await
    });
    let result = handle.await.unwrap();
    println!("{}", result);
}

11. 常用 Crate 介绍

Crate 用途 版本建议
serde / serde_json 序列化/反序列化 1.0
tokio 异步运行时 1.x
reqwest HTTP 客户端 0.12
axum Web 框架(Tokio 生态) 0.7
actix-web Web 框架(高性能) 4.x
sqlx 异步数据库(编译期校验 SQL) 0.8
clap CLI 参数解析 4.x
tracing 结构化日志 0.1
anyhow / thiserror 错误处理 1.x
chrono 日期时间 0.4
regex 正则表达式 1.x
rand 随机数 0.8

12. Web 开发实战:Axum 与 Actix-web

12.1 Axum 入门

# Cargo.toml
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
use axum::{
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};

#[derive(Serialize)]
struct Message {
    message: String,
}

#[derive(Deserialize)]
struct CreateUser {
    name: String,
    email: String,
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

async fn hello() -> Json<Message> {
    Json(Message {
        message: "Hello, Axum!".to_string(),
    })
}

async fn create_user(Json(payload): Json<CreateUser>) -> Json<User> {
    Json(User {
        id: 1,
        name: payload.name,
        email: payload.email,
    })
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(hello))
        .route("/users", post(create_user));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();
    println!("Server running on http://localhost:3000");
    axum::serve(listener, app).await.unwrap();
}

12.2 Axum 中间件与层

use axum::{
    middleware::{self, Next},
    response::Response,
    routing::get,
    Router,
};
use std::time::Instant;
use tower_http::cors::CorsLayer;

async fn logging_middleware(
    req: axum::http::Request<axum::body::Body>,
    next: Next,
) -> Response {
    let method = req.method().clone();
    let uri = req.uri().clone();
    let start = Instant::now();

    let response = next.run(req).await;

    println!("{} {} - {} - {:?}",
        method, uri, response.status(), start.elapsed());
    response
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(|| async { "Hello!" }))
        .layer(middleware::map_fn(logging_middleware))
        .layer(CorsLayer::permissive());

    // ...
}

12.3 Actix-web 对比

use actix_web::{web, App, HttpServer, HttpResponse, Responder};
use serde::Deserialize;

#[derive(Deserialize)]
struct Info {
    name: String,
}

async fn greet(info: web::Path<Info>) -> impl Responder {
    format!("Hello {}!", info.name)
}

async fn index() -> HttpResponse {
    HttpResponse::Ok().body("Welcome!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(index))
            .route("/hello/{name}", web::get().to(greet))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Axum vs Actix-web 选择建议

  • Axum:Tokio 官方生态,设计现代,与 Tower 中间件生态深度集成,社区活跃。
  • Actix-web:老牌高性能框架,benchmark 表现优秀,API 更紧凑。

13. CLI 工具开发

使用 clap 构建命令行工具:

[dependencies]
clap = { version = "4", features = ["derive"] }
use clap::Parser;

/// A simple file word counter
#[derive(Parser, Debug)]
#[command(author, version, about)]
struct Args {
    /// Input file path
    #[arg(short, long)]
    file: String,

    /// Show line count instead
    #[arg(short, long, default_value_t = false)]
    lines: bool,

    /// Minimum word length to count
    #[arg(short, long, default_value_t = 1)]
    min_length: usize,
}

fn main() {
    let args = Args::parse();

    let content = std::fs::read_to_string(&args.file)
        .expect("Failed to read file");

    if args.lines {
        let count = content.lines().count();
        println!("Lines: {}", count);
    } else {
        let count = content.split_whitespace()
            .filter(|w| w.len() >= args.min_length)
            .count();
        println!("Words (min length {}): {}", args.min_length, count);
    }
}

运行:

cargo run -- --file README.md
cargo run -- --file README.md --lines
cargo run -- --file README.md --min-length 5

14. FFI 与跨语言调用

14.1 Rust 调用 C 函数

// 声明外部 C 函数
extern "C" {
    fn abs(input: i32) -> i32;
    fn sqrt(input: f64) -> f64;
}

fn main() {
    unsafe {
        println!("abs(-5) = {}", abs(-5));
        println!("sqrt(144) = {}", sqrt(144.0));
    }
}

14.2 Rust 导出给 C/Python 调用

// lib.rs
#[no_mangle]
pub extern "C" fn add_numbers(a: i32, b: i32) -> i32 {
    a + b
}

#[no_mangle]
pub extern "C" fn greet(name: *const std::ffi::c_char) -> *mut std::ffi::c_char {
    let c_str = unsafe { std::ffi::CStr::from_ptr(name) };
    let greeting = format!("Hello, {}!", c_str.to_str().unwrap_or("World"));
    std::ffi::CString::new(greeting).unwrap().into_raw()
}

Cargo.toml 中配置生成动态库:

[lib]
crate-type = ["cdylib", "staticlib"]

14.3 Python 调用 Rust(PyO3)

[dependencies]
pyo3 = { version = "0.22", features = ["extension-module"] }

[lib]
name = "my_rust_lib"
crate-type = ["cdylib"]
use pyo3::prelude::*;

#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
    Ok((a + b).to_string())
}

#[pymodule]
fn my_rust_lib(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
    Ok(())
}

Python 端使用:

import my_rust_lib
result = my_rust_lib.sum_as_string(5, 20)
print(result)  # "25"

15. 学习资源与进阶路径

15.1 推荐学习路线

  1. 入门The Rust Programming Language(官方书籍,免费在线阅读)
  2. 练习Rustlings(小练习)
  3. 进阶Rust by Example
  4. 深入The Rustonomicon(unsafe Rust)
  5. 异步Asynchronous Programming in Rust

15.2 社区与生态

15.3 实战项目建议

难度 项目 技能点
命令行计算器 基础语法、模式匹配
⭐⭐ 文件搜索工具(类似 grep) 文件 I/O、正则、CLI
⭐⭐⭐ HTTP 服务器 TCP、HTTP 协议、多线程
⭐⭐⭐⭐ 数据库 ORM 泛型、trait、宏
⭐⭐⭐⭐⭐ 分布式 KV 存储 网络、序列化、Raft 协议

总结

Rust 的学习曲线确实陡峭,尤其是所有权和生命周期系统——但一旦掌握,你会发现编译器成为了你最可靠的搭档。它在编译期帮你捕获的 bug,比你在运行期花数小时调试的问题要少得多。

核心记住

  • 所有权系统是 Rust 的灵魂,理解它就理解了 Rust 的一半
  • Result + ? 是错误处理的标准范式
  • trait + 泛型 实现零成本抽象
  • async/await + Tokio 是异步编程的标准方案
  • Cargo 是你最好的朋友

祝你在 Rust 的世界里写出让编译器微笑的代码。🦀

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录