Rust 完整指南:从安装到打包发布
2026/7/24 21:20:28 网站建设 项目流程

引言

Rust 是一门系统级编程语言,由 Mozilla 研究院开发,旨在提供内存安全、并发安全和高性能。它没有垃圾回收器,而是通过所有权系统在编译时保证内存安全。Rust 的语法类似 C/C++,但融入了许多现代语言特性,如模式匹配、类型推断、trait 系统等。近年来,Rust 在开发者社区中越来越受欢迎,被广泛应用于操作系统、嵌入式、WebAssembly、网络服务等领域。

本指南将从零开始,带你全面了解 Rust 的安装、语法、核心概念、工具链、项目组织,一直到如何将你的 crate 发布到 crates.io。无论你是初学者还是有一定经验的开发者,都能从中受益。


安装 Rust

Rust 官方提供了便捷的安装工具rustup,它可以管理 Rust 版本和相关的工具链。我们将介绍在主流操作系统上的安装步骤。

在 Windows 上安装

  1. 访问 https://rustup.rs,下载rustup-init.exe

  2. 运行下载的可执行文件,会出现一个命令行界面,提示选择安装选项。通常选择默认安装(键入 1 并回车)即可。

  3. 安装程序会自动下载并安装 Rust 编译器(rustc)、包管理器 Cargo、以及文档等组件。

  4. 安装完成后,需要重启终端或手动刷新环境变量,以便rustccargo命令可用。

此外,Rust 依赖于 Microsoft C++ 构建工具。如果你在安装后尝试编译代码时遇到链接错误,可能需要安装 Visual Studio Build Tools 或 Visual Studio(包含 C++ 工具链)。

在 macOS 上安装

macOS 用户可以通过终端运行以下命令安装:

bash

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

该脚本会下载并运行rustup-init,同样会有交互式选项,选择默认安装即可。安装程序会自动将~/.cargo/bin添加到 PATH。之后你需要重启终端或执行source ~/.cargo/env使配置生效。

在 Linux 上安装

在 Linux 上,同样使用上述curl命令进行安装。大多数 Linux 发行版需要安装编译器和链接器(如gccmake等)。例如在 Ubuntu 上:

bash

sudo apt update sudo apt install build-essential curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装完成后,同样需要将~/.cargo/bin加入 PATH。

验证安装

安装完成后,打开新终端并执行:

bash

rustc --version cargo --version

如果看到类似rustc 1.70.0 (90c541806 2023-05-31)的输出,说明安装成功。

更新和卸载 Rust

  • 更新 Rust 工具链:

    bash

    rustup update
  • 卸载 Rust:

    bash

    rustup self uninstall

第一个 Rust 程序

Hello, World!

按照传统,我们从 Hello, World! 开始。创建一个新目录,比如hello_world,并在其中新建一个文件main.rs,内容如下:

rust

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

在终端进入该目录,执行:

bash

rustc main.rs ./main # 在 Windows 上是 .\main.exe

你会在屏幕上看到 "Hello, world!"。这里rustc直接编译了单个文件,但在实际项目中我们通常使用 Cargo。

使用 Cargo 创建项目

Cargo 是 Rust 的构建系统和包管理器。创建一个新项目:

bash

cargo new hello_cargo cd hello_cargo

Cargo 会生成如下目录结构:

text

hello_cargo/ Cargo.toml src/ main.rs
  • Cargo.toml是项目的配置文件,类似于package.json

  • src/main.rs是源代码文件。

默认生成的main.rs已经包含一个 "Hello, world!" 程序。构建并运行:

bash

cargo build # 编译,生成可执行文件在 target/debug/hello_cargo cargo run # 编译并运行

你也可以使用cargo check快速检查代码是否通过编译,而不生成可执行文件。


基础语法

变量与可变性

Rust 中变量默认是不可变的。使用let声明变量:

rust

let x = 5; // 不可变 x = 6; // 错误!不能对不可变变量二次赋值

若希望变量可变,需添加mut关键字:

rust

let mut y = 5; y = 6; // 允许

常量使用const声明,必须注明类型,且值必须在编译时确定:

rust

const MAX_POINTS: u32 = 100_000;

变量可以隐藏(shadowing),即重新使用let声明同名变量,新变量会覆盖旧变量:

rust

let x = 5; let x = x + 1; // 新变量 x 为 6,原 x 被隐藏

数据类型

Rust 是静态类型语言,但编译器通常能推断类型。主要分为标量(scalar)和复合(compound)类型。

标量类型
  • 整数:有符号i8, i16, i32, i64, i128, isize(与架构相关),无符号u8, u16, u32, u64, u128, usize。默认i32

  • 浮点数:f32, f64。默认f64

  • 布尔:bool,值为truefalse

  • 字符:char,用单引号,Unicode 标量值,如'a''😻'

复合类型
  • 元组:不同类型值的集合,长度固定。如let tup: (i32, f64, u8) = (500, 6.4, 1);。可以通过模式匹配或索引访问:tup.0

  • 数组:相同类型值的集合,长度固定。如let arr = [1, 2, 3];。访问:arr[0]。数组存储在栈上,若需要动态大小,应使用 Vector。

函数

使用fn定义函数,参数需注明类型,返回值类型在箭头后注明:

rust

fn add(x: i32, y: i32) -> i32 { x + y // 表达式,不加分号即为返回值 }

最后一个表达式就是返回值,也可使用return提前返回。

注释

  • 普通注释:///* ... */

  • 文档注释:///用于生成 API 文档,支持 Markdown。

控制流

if 表达式

rust

let number = 3; if number < 5 { println!("condition was true"); } else if number == 5 { println!("condition was five"); } else { println!("condition was false"); }

if是表达式,可以用于赋值:

rust

let condition = true; let number = if condition { 5 } else { 6 };
循环
  • loop:无限循环,可用break退出,并可返回值。

    rust

    let result = loop { counter += 1; if counter == 10 { break counter * 2; } };
  • while:条件循环。

  • for:遍历集合,最常用。

    rust

    let a = [10, 20, 30]; for element in a.iter() { println!("value: {}", element); } // 或使用范围 for number in 1..4 { println!("{}", number); // 1 2 3 }

所有权系统

所有权是 Rust 最独特的特性,它让 Rust 无需垃圾回收即可保证内存安全。

所有权规则

  1. Rust 中每一个值都有一个被称为其所有者的变量。

  2. 值在任一时刻有且只有一个所有者。

  3. 当所有者(变量)离开作用域,这个值将被丢弃。

内存与分配

对于栈上数据(如标量),拷贝是廉价的;对于堆上数据(如String),当变量离开作用域时,Rust 会自动调用drop释放内存。考虑以下代码:

rust

let s1 = String::from("hello"); let s2 = s1; // s1 的所有权被移动到 s2,s1 不再有效 println!("{}", s1); // 错误!s1 已被移动

若想深拷贝堆数据,可以使用clone

rust

let s1 = String::from("hello"); let s2 = s1.clone(); // 堆数据被复制 println!("s1 = {}, s2 = {}", s1, s2); // 都有效

对于栈上数据,如整数,实现了Copytrait,赋值时会自动拷贝:

rust

let x = 5; let y = x; // x 仍然有效,因为整数是 Copy 的

引用与借用

如果我们想使用值但不获取所有权,可以使用引用。引用默认也是不可变的。

rust

fn calculate_length(s: &String) -> usize { // s 是对 String 的引用 s.len() } let s1 = String::from("hello"); let len = calculate_length(&s1); // 传递引用,s1 所有权未移动

通过引用传递参数称为借用。引用默认不可变,但可以创建可变引用:

rust

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

注意:在特定作用域中,对特定数据只能有一个可变引用,且不能同时拥有可变和不可变引用。这防止了数据竞争。

切片

切片是集合中一部分连续元素的引用,它没有所有权。例如字符串切片:

rust

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

数组切片类似:let slice = &arr[1..3];

字符串字面量本身就是切片:let s: &str = "Hello";


结构体与方法

定义结构体

使用struct关键字定义:

rust

struct User { username: String, email: String, sign_in_count: u64, active: bool, }

实例化结构体

rust

let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, };

可以使用点号访问字段。如果变量名与字段名相同,可以用字段初始化简写:

rust

fn build_user(email: String, username: String) -> User { User { email, username, active: true, sign_in_count: 1, } }

结构体更新语法:

rust

let user2 = User { email: String::from("another@example.com"), ..user1 // 其余字段从 user1 获取,注意 user1 的部分字段可能被移动 };

元组结构体(类似元组但有名):

rust

struct Color(i32, i32, i32); let black = Color(0, 0, 0);

单元结构体(无字段):

rust

struct Empty;

方法语法

方法在impl块中定义,第一个参数总是self(或其变体&self&mut self),代表调用该方法的实例。

rust

impl User { fn is_active(&self) -> bool { self.active } } let user1 = User { ... }; println!("{}", user1.is_active());

关联函数

impl中定义不带self参数的函数,称为关联函数,通常用作构造函数,例如String::from

rust

impl User { fn new(email: String, username: String) -> User { User { email, username, active: true, sign_in_count: 1, } } } let user = User::new(String::from("..."), String::from("..."));

枚举与模式匹配

枚举定义

枚举允许你定义一个类型,该类型的值只能是几个变体之一:

rust

enum IpAddrKind { V4, V6, } let four = IpAddrKind::V4;

每个变体可以关联不同类型的数据:

rust

enum IpAddr { V4(u8, u8, u8, u8), V6(String), } let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1"));

枚举也可以定义方法。

Option 枚举

Rust 没有空值,而是使用Option<T>枚举来表示值存在或不存在:

rust

enum Option<T> { Some(T), None, }

它被包含在预导入模块中,可以直接使用SomeNone

rust

let some_number = Some(5); let absent_number: Option<i32> = None;

使用Option<T>时,必须处理None的情况,避免空指针异常。

match 控制流

match允许将一个值与一系列模式比较,并执行匹配模式对应的代码。模式可以是字面值、变量名、通配符等。

rust

enum Coin { Penny, Nickel, Dime, Quarter, } fn value_in_cents(coin: Coin) -> u8 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, } }

匹配必须是穷尽的。如果不想处理所有情况,可以使用_通配符:

rust

let some_u8_value = 0u8; match some_u8_value { 1 => println!("one"), 3 => println!("three"), _ => (), }

if let 简洁控制

当你只关心某一种模式而忽略其他时,if let更简洁:

rust

let some_u8_value = Some(0u8); if let Some(3) = some_u8_value { println!("three"); } else { // 可选 }

模块系统

Rust 提供了一套模块系统来组织代码:包(package)、crate(单元包)、模块(module)和路径(path)。

包、crate 与模块

  • crate是一个二进制项或库。crate root是源代码文件,Cargo 会依此编译。

  • 是提供一系列功能的一个或多个 crate。包含一个Cargo.toml文件,描述如何构建这些 crate。

  • 模块用于在 crate 内组织代码,控制私有性。

默认情况下,所有项(函数、结构体等)都是私有的。使用pub关键字公开。

路径

路径有两种形式:

  • 绝对路径:从 crate root 开始,使用 crate 名或字面量crate

  • 相对路径:从当前模块开始,使用selfsuper或当前模块的标识符。

例如,在库 crate 中:

rust

mod front_of_house { pub mod hosting { pub fn add_to_waitlist() {} } } pub fn eat_at_restaurant() { // 绝对路径 crate::front_of_house::hosting::add_to_waitlist(); // 相对路径 front_of_house::hosting::add_to_waitlist(); }

use 关键字

使用use将路径引入作用域,类似于import

rust

use crate::front_of_house::hosting; // 或者使用相对路径 // use self::front_of_house::hosting; pub fn eat_at_restaurant() { hosting::add_to_waitlist(); }

习惯上,对于函数,引入父模块;对于结构体、枚举等,引入完整路径。也可以使用as重命名。

rust

use std::fmt::Result; use std::io::Result as IoResult;

pub use可以重导出项,使外部代码能访问原本私有的项。

模块分割文件

当模块变大时,可以将它们放入单独的文件。在src/lib.rssrc/main.rs中:

rust

mod front_of_house; // 告诉 Rust 从 front_of_house.rs 或 front_of_house/mod.rs 加载模块

然后在src/front_of_house.rs中定义模块内容。如果模块还有子模块,可以创建front_of_house目录和mod.rs


常用集合

Vector

Vec<T>是动态数组,可存储多个相同类型的值。

rust

let mut v: Vec<i32> = Vec::new(); v.push(5); v.push(6); // 使用 vec! 宏创建 let v = vec![1, 2, 3]; // 读取元素 let third: &i32 = &v[2]; // 索引,越界 panic match v.get(2) { // 返回 Option<&T> Some(third) => println!("third element is {}", third), None => println!("no third element"), } // 遍历 for i in &v { println!("{}", i); } for i in &mut v { *i += 50; // 解引用修改 }

字符串

Rust 的字符串是String(可增长、可变、拥有所有权)和&str(字符串切片)。String内部使用 UTF-8 编码。

rust

let mut s = String::new(); let data = "initial contents"; let s = data.to_string(); // 或 "initial contents".to_string() let s = String::from("initial contents"); // 更新 s.push_str("bar"); // 追加字符串切片 s.push('l'); // 追加字符 let s1 = String::from("Hello, "); let s2 = String::from("world!"); let s3 = s1 + &s2; // s1 被移动,不能再使用 // 格式化宏 let s = format!("{}-{}", s1, s2); // 不支持索引访问,因为 UTF-8 编码。需使用 chars() 或 bytes() for c in "你好".chars() { println!("{}", c); // 你 好 } for b in "你好".bytes() { println!("{}", b); // 输出字节 }

HashMap

HashMap<K, V>存储键值对。

rust

use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); // 通过 collect 从元组向量创建 let teams = vec![String::from("Blue"), String::from("Yellow")]; let initial_scores = vec![10, 50]; let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect(); // 访问 let score = scores.get("Blue"); // 返回 Option<&V> // 遍历 for (key, value) in &scores { println!("{}: {}", key, value); } // 更新:insert 会覆盖;entry 处理不存在时插入 scores.entry(String::from("Blue")).or_insert(50); // 仅当不存在时插入

错误处理

Rust 将错误分为两类:可恢复错误(如文件未找到)和不可恢复错误(如数组越界)。

panic! 不可恢复错误

当程序遇到无法处理的错误时,可以调用panic!宏,程序会打印错误信息、展开并清理栈(或直接中止)。

rust

panic!("crash and burn");

可以通过设置环境变量RUST_BACKTRACE=1获取回溯信息。

Result 可恢复错误

Result枚举定义如下:

rust

enum Result<T, E> { Ok(T), Err(E), }

常用于可能失败的操作:

rust

use std::fs::File; use std::io::ErrorKind; let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create("hello.txt") { Ok(fc) => fc, Err(e) => panic!("Problem creating the file: {:?}", e), }, other_error => panic!("Problem opening the file: {:?}", other_error), }, };

传播错误

将错误返回给调用者处理:

rust

use std::io; use std::io::Read; use std::fs::File; fn read_username_from_file() -> Result<String, io::Error> { let f = File::open("hello.txt"); let mut f = match f { Ok(file) => file, Err(e) => return Err(e), }; let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } }

使用?运算符简化:

rust

fn read_username_from_file() -> Result<String, io::Error> { let mut f = File::open("hello.txt")?; let mut s = String::new(); f.read_to_string(&mut s)?; Ok(s) }

?会从Result中取出值,若为Err则提前返回。也可链式调用:

rust

fn read_username_from_file() -> Result<String, io::Error> { let mut s = String::new(); File::open("hello.txt")?.read_to_string(&mut s)?; Ok(s) }

?还可以用于Option类型,若为None则提前返回None

何时使用 panic

在示例、原型代码和测试中,可以使用panic!。在能确保不会出现Err的情况下也可以使用unwrapexpect。对于库代码,应该将错误返回给调用者。


泛型、trait 与生命周期

泛型函数和结构体

泛型允许定义适用于多种类型的函数或结构体:

rust

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { let mut largest = list[0]; for &item in list { if item > largest { largest = item; } } largest } struct Point<T> { x: T, y: T, } impl<T> Point<T> { fn x(&self) -> &T { &self.x } }

trait 定义与实现

trait 类似于接口,定义共享行为:

rust

pub trait Summary { fn summarize(&self) -> String; } pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } impl Summary for NewsArticle { fn summarize(&self) -> String { format!("{}, by {} ({})", self.headline, self.author, self.location) } }

trait 作为参数

可以接受实现了某个 trait 的类型:

rust

pub fn notify(item: &impl Summary) { println!("Breaking news! {}", item.summarize()); }

等价于 trait bound 语法:

rust

pub fn notify<T: Summary>(item: &T) { println!("Breaking news! {}", item.summarize()); }

多个 trait 使用+

rust

pub fn notify(item: &(impl Summary + Display)) {}

复杂 bound 可使用where子句:

rust

fn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug, { // ... }

trait 也可以作为返回类型(但只能返回单一类型):

rust

fn returns_summarizable() -> impl Summary { NewsArticle { ... } }

生命周期注解

生命周期确保引用始终有效。它们也是泛型的一种。

rust

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }

这里'a是生命周期参数,表示返回的引用与两个参数中的较小生命周期一致。

结构体中也可包含引用,需要生命周期注解:

rust

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

生命周期省略规则

在某些情况下,Rust 编译器可以自动推断生命周期,这称为生命周期省略规则。例如:

  • 每个引用参数都有自己的生命周期参数。

  • 如果只有一个输入生命周期,它会被赋给所有输出生命周期。

  • 如果有多个输入生命周期,但其中一个为&self&mut self,则self的生命周期赋给所有输出。


编写测试

测试函数

在 Rust 中,测试是通过#[test]属性标记的函数。运行cargo test会执行所有测试函数。

rust

#[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } #[test] fn another() { panic!("Make this test fail"); } }

断言

  • assert!(expression):若表达式为 false 则 panic。

  • assert_eq!(left, right)assert_ne!(left, right):比较两个值,若不等/等则 panic。

  • 可添加自定义消息:assert_eq!(a, b, "a 不等于 b, a={}, b={}", a, b)

测试组织

单元测试通常放在与代码相同的文件中,使用#[cfg(test)]条件编译。集成测试放在tests目录下,每个文件被视为单独的 crate,只能测试公共 API。


智能指针

智能指针是实现了DerefDroptrait 的数据结构,它们拥有数据的所有权并提供额外功能。

Box<T>

Box<T>用于在堆上分配值,最简单直接。适合递归类型、在需要特定大小的地方使用。

rust

let b = Box::new(5); println!("b = {}", b); // 可以像普通引用一样使用 // 递归类型 Cons list enum List { Cons(i32, Box<List>), Nil, }

Deref 和 Drop trait

  • Deref允许智能指针像普通引用一样解引用(*v)。Rust 会自动解引用以匹配类型。

  • Drop允许自定义值离开作用域时的清理代码。

Rc<T>、RefCell<T> 和内部可变性

  • Rc<T>是引用计数指针,允许多个所有者(只读)。

  • RefCell<T>在运行时检查借用规则,实现内部可变性,即即使RefCell<T>本身不可变,也能修改其内部值。

rust

use std::rc::Rc; use std::cell::RefCell; let value = Rc::new(RefCell::new(5)); let a = Rc::clone(&value); let b = Rc::clone(&value); *value.borrow_mut() += 10; // 修改内部值 println!("{:?}", value); // RefCell { value: 15 }

循环引用

Rc<T>RefCell<T>可能导致循环引用和内存泄漏。可以使用Weak<T>打破循环。


并发编程

线程

使用thread::spawn创建新线程:

rust

use std::thread; use std::time::Duration; let handle = thread::spawn(|| { for i in 1..10 { println!("hi number {} from the spawned thread!", i); thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { println!("hi number {} from the main thread!", i); thread::sleep(Duration::from_millis(1)); } handle.join().unwrap(); // 等待子线程结束

消息传递

使用通道(channel)在线程间通信:

rust

use std::sync::mpsc; use std::thread; let (tx, rx) = mpsc::channel(); thread::spawn(move || { let val = String::from("hi"); tx.send(val).unwrap(); }); let received = rx.recv().unwrap(); // 阻塞接收 println!("Got: {}", received);

共享状态

使用互斥器Mutex<T>和原子引用计数Arc<T>实现线程间共享数据:

rust

use std::sync::{Arc, Mutex}; use std::thread; 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());

Send 和 Sync trait

  • Send允许在线程间转移所有权。几乎所有 Rust 类型都是Send,但Rc<T>不是。

  • Sync允许多线程同时访问。实现了Sync的类型,其引用&TSend。基本类型都是Sync,但RefCell<T>不是,Mutex<T>是。


面向对象特性

trait 对象

trait 对象允许动态分发,实现类似多态的效果。需要使用dyn关键字。

rust

pub trait Draw { fn draw(&self); } pub struct Screen { pub components: Vec<Box<dyn Draw>>, } impl Screen { pub fn run(&self) { for component in self.components.iter() { component.draw(); } } }

这里Box<dyn Draw>是一个 trait 对象,它可以指向任何实现了Draw的类型。

实现多态

可以为不同类型实现同一个 trait,然后通过 trait 对象统一调用:

rust

struct Button { width: u32, height: u32, label: String, } impl Draw for Button { fn draw(&self) { // 绘制按钮 } } struct SelectBox { width: u32, height: u32, options: Vec<String>, } impl Draw for SelectBox { fn draw(&self) { // 绘制选择框 } } fn main() { let screen = Screen { components: vec![ Box::new(SelectBox { ... }), Box::new(Button { ... }), ], }; screen.run(); }

模式与匹配进阶

模式语法

模式可以出现在matchif letwhile letfor循环、let语句、函数参数等位置。常见模式:

  • 字面值:1"abc"

  • 变量名:x(会绑定值)

  • 通配符_

  • 范围..=1..=5

  • 解构结构体、元组、枚举

  • 引用模式:&vref vref mut v

  • 匹配守卫:在模式后添加if条件

示例:

rust

let x = 5; match x { 1 => println!("one"), 2 | 3 | 5 => println!("two, three or five"), 6..=10 => println!("six to ten"), _ => println!("something else"), } let p = Point { x: 0, y: 7 }; match p { Point { x, y: 0 } => println!("on the x axis at {}", x), Point { x: 0, y } => println!("on the y axis at {}", y), Point { x, y } => println!("on neither axis: ({}, {})", x, y), }

高级特性

不安全 Rust

不安全 Rust 允许你进行一些编译器无法保证安全的操作,如解引用裸指针、调用不安全函数、访问可变静态变量、实现不安全 trait。使用unsafe关键字标记代码块或函数。

rust

unsafe fn dangerous() {} let mut num = 5; let r1 = &num as *const i32; // 裸指针 unsafe { dangerous(); println!("r1 is: {}", *r1); // 解引用裸指针 }

应尽量将不安全代码封装在安全抽象中。

高级 trait

  • 关联类型:在 trait 中指定一个类型占位符。

    rust

    pub trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; }
  • 默认泛型参数和运算符重载:std::ops中的 trait 如Add支持重载+

  • 完全限定语法:当多个 trait 有同名方法时,使用<Type as Trait>::method(...)

高级类型

  • 类型别名:type Kilometers = i32;

  • 动态大小类型和Sizedtrait:如str是动态大小,只能通过&str使用。泛型默认是Sized,可以用?Sized放宽。

宏分为声明宏和过程宏。声明宏使用macro_rules!定义,类似于模式匹配生成代码。过程宏接收代码作为输入,操作后输出代码,有三种:自定义#[derive]宏、类属性宏、类函数宏。

示例声明宏:

rust

macro_rules! say_hello { () => { println!("Hello!"); }; } say_hello!();

异步编程

Rust 的异步编程基于Futuretrait 和async/.await语法。

async/.await

使用async fn定义异步函数,返回一个实现了Future的类型。在 async 函数内部可以使用.await等待另一个 Future 完成。

rust

async fn hello() { println!("Hello, async!"); } #[tokio::main] async fn main() { hello().await; }

Futures

Future代表一个可能尚未完成的计算。Rust 的 Future 是惰性的,只有被轮询才会推进。通常需要一个执行器(executor)来驱动 Future。

异步运行时

常用的异步运行时是 Tokio 和 async-std。它们提供了 I/O、计时器、任务生成等功能。

使用 Tokio:

rust

use tokio::time; #[tokio::main] async fn main() { let handle = tokio::spawn(async { time::sleep(time::Duration::from_secs(1)).await; println!("task finished"); }); handle.await.unwrap(); }

项目结构与工作空间

工作空间

工作空间是一系列共享同一个 Cargo.lock 和输出目录的包。在根目录创建Cargo.toml

toml

[workspace] members = [ "adder", "add-one", ]

每个成员都是一个独立的 crate。成员之间可以通过路径依赖。


依赖管理

Cargo.toml

Cargo.toml[dependencies]部分添加依赖:

toml

[dependencies] rand = "0.8.5"

Cargo 会从 crates.io 下载相应版本。

版本指定

版本号遵循语义化版本。可以使用^~*<>等指定范围。例如rand = "0.8"相当于^0.8,允许兼容 0.8.x 但不包括 0.9.0。

依赖覆盖

可以使用[patch][replace]覆盖依赖来源,例如使用本地路径或 git 仓库。


构建脚本与自定义构建

如果需要在编译前执行一些代码(如生成代码、链接系统库),可以在项目根目录创建build.rs构建脚本。它会在编译 crate 前被编译并执行。

rust

// build.rs fn main() { println!("cargo:rustc-link-search=native=/usr/local/lib"); }

构建脚本可以通过println!输出指令给 Cargo。


文档编写

文档注释

文档注释使用/////!。支持 Markdown,并会生成 HTML 文档。

rust

/// Adds one to the number given. /// /// # Examples /// /// ``` /// let arg = 5; /// let answer = my_crate::add_one(arg); /// assert_eq!(6, answer); /// ``` pub fn add_one(x: i32) -> i32 { x + 1 }

# Examples部分的代码会作为测试运行。

生成文档

运行cargo doc会在target/doc生成文档。cargo doc --open会在浏览器打开。


发布到 crates.io

创建账号

访问 crates.io,使用 GitHub 账号登录,然后在本地使用cargo login <API_TOKEN>登录。

打包

在发布前,确保Cargo.toml中有必要的元数据:

toml

[package] name = "my_crate" version = "0.1.0" authors = ["Your Name <you@example.com>"] edition = "2021" description = "A short description of my crate" license = "MIT OR Apache-2.0" repository = "https://github.com/yourname/my_crate"

运行cargo publish --dry-run检查。然后执行cargo publish发布。

发布与版本管理

发布后无法删除,但可以发布新版本。修改版本号后再运行cargo publish


FFI 与外部语言接口

调用 C 函数

使用extern块声明外部函数,并链接到相应库。

rust

extern "C" { fn abs(input: i32) -> i32; } fn main() { unsafe { println!("Absolute value of -3 according to C: {}", abs(-3)); } }

需要在构建脚本或通过links键指定库。

从 C 调用 Rust

将 Rust 函数标记为#[no_mangle]并使用extern "C"导出,编译为静态库或动态库。

rust

#[no_mangle] pub extern "C" fn call_from_c() { println!("Just called a Rust function from C!"); }

Cargo.toml中设置 crate 类型:

toml

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

C 代码可以链接生成的库并调用函数。


常用开发工具

rustfmt 和 clippy

  • rustfmt自动格式化代码:cargo fmt

  • clippy提供更多 lint 检查:cargo clippy

rust-analyzer

rust-analyzer 是 Rust 的语言服务器,提供代码补全、跳转定义等功能。主流编辑器(VS Code、Vim/Neovim、Emacs)都支持。


生态与常用库

Rust 拥有丰富且活跃的生态系统。以下是一些常用库:

  • Web 框架:Actix-web、Rocket、Warp

  • 异步运行时:Tokio、async-std

  • 序列化:serde(与 JSON、YAML、TOML 等配合)

  • HTTP 客户端:reqwest

  • 数据库驱动:sqlx(异步)、diesel(ORM)

  • 日志:log、env_logger

  • 命令行解析:clap

  • 错误处理:anyhow、thiserror

  • 测试:proptest、quickcheck

  • 嵌入式:embedded-hal

  • WebAssembly:wasm-bindgen

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询