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)
}

在实际项目中,推荐使用 thiserror 或 anyhow 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 与我们交流。

目录