Skip to content

Rust

Rust圣经 Rusty Book(锈书)

在 Windows 上自定义 Rust 安装路径

1. 设置环境变量

在 Windows 上,你可以通过设置环境变量来指定 Rust 的安装路径。需要设置以下两个环境变量:

  • CARGO_HOME:指定 Cargo 的安装目录(包括 cargo 可执行文件、缓存、配置文件等)。
  • RUSTUP_HOME:指定 Rustup 的安装目录(包括 Rust 工具链、配置文件等)。
方法 1:通过命令提示符临时设置

打开命令提示符(CMD)或 PowerShell,运行以下命令:

bash
set CARGO_HOME=C:\path\to\cargo
set RUSTUP_HOME=C:\path\to\rustup
#或者
cd D:\devel\rust #进入安装的目录
set RUSTUP_HOME=%CD%\.rustup
set CARGO_HOME=%CD%\.cargo
方法 2:永久设置环境变量
  1. 右键点击“此电脑”或“我的电脑”,选择“属性”。

  2. 点击“高级系统设置”。

  3. 在“系统属性”窗口中,点击“环境变量”。

  4. 在“用户变量”或“系统变量”中,点击“新建”:

    • 变量名:CARGO_HOME,变量值:C:\path\to\cargo

    • 变量名:RUSTUP_HOME,变量值:C:\path\to\rustup

      C:\path\to替换成你要的安装路径

  5. 点击“确定”保存。

2. 安装 Rust

设置好环境变量后,下载并运行 Rust 安装程序:

  1. 访问 Rust 官方安装页面:https://www.rust-lang.org/tools/install
  2. 下载 rustup-init.exe
  3. 运行 rustup-init.exe,按照提示完成安装。

安装程序会自动检测你设置的环境变量,并将 Rust 安装到指定的路径。

3. 验证安装

安装完成后,打开命令提示符或 PowerShell,运行以下命令验证安装:

bash
rustc --version
cargo --version

如果输出了 Rust 和 Cargo 的版本信息,说明安装成功。


示例

假设你想将 Rust 安装到 D:\Rust 目录下:

  1. 设置环境变量:
    • CARGO_HOME = D:\Rust\cargo
    • RUSTUP_HOME = D:\Rust\rustup
  2. 运行 rustup-init.exe 完成安装。
  3. 安装完成后,Rust 工具链会安装在 D:\Rust\cargoD:\Rust\rustup 目录中。

注意事项

  1. 路径不要包含空格或特殊字符:尽量使用简单的路径,例如 D:\Rust,避免使用 C:\Program Files 这样的路径,因为空格可能会导致一些问题。

  2. 环境变量生效:如果你在安装前设置了环境变量,请确保它们已生效。可以通过以下命令检查:

    bash
    echo %CARGO_HOME%
    echo %RUSTUP_HOME%
  3. 移动已安装的 Rust:如果你已经安装了 Rust,可以通过手动移动 %USERPROFILE%\.cargo%USERPROFILE%\.rustup 目录到新路径,并更新环境变量。

Linux/macOS 系统 上自定义 Rust 安装路径

  1. 设置 CARGO_HOMERUSTUP_HOME 环境变量

    • CARGO_HOME:用于指定 Cargo 的安装目录(包括 cargo 可执行文件、缓存、配置文件等)。
    • RUSTUP_HOME:用于指定 Rustup 的安装目录(包括 Rust 工具链、配置文件等)。

    你可以在安装 Rust 之前设置这些环境变量,或者在安装后通过修改环境变量来移动这些目录。

  2. 安装 Rust

    在设置好环境变量后,使用 rustup 安装 Rust。

    bash
    export CARGO_HOME=/custom/path/to/cargo
    export RUSTUP_HOME=/custom/path/to/rustup
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

    这将把 Rust 工具链安装到你指定的路径中。

  3. 验证安装

    安装完成后,你可以通过以下命令验证 Rust 是否安装成功:

    bash
    source $CARGO_HOME/env
    rustc --version
    cargo --version

示例

假设你想将 Rust 安装到 /opt/rust 目录下:

bash
export CARGO_HOME=/opt/rust/cargo
export RUSTUP_HOME=/opt/rust/rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装完成后,Rust 工具链将会安装在 /opt/rust/cargo/opt/rust/rustup 目录中。

注意事项

  • 如果你已经安装了 Rust,并且想要移动安装目录,你需要手动将 ~/.cargo~/.rustup 目录移动到新的位置,并更新环境变量。
  • 确保你设置的环境变量在每次启动终端时都能生效。你可以将这些环境变量添加到你的 shell 配置文件(如 ~/.bashrc~/.zshrc 等)中。
bash
echo 'export CARGO_HOME=/custom/path/to/cargo' >> ~/.bashrc
echo 'export RUSTUP_HOME=/custom/path/to/rustup' >> ~/.bashrc
source ~/.bashrc

这样,每次启动终端时,环境变量都会自动设置好。

命令

项目运行:

shell
cargo run

高性能运行:

shell
cargo run --release

检查一下代码能否编译通过:

shell
cargo check

项目构建:

bash
cargo build

该命令将下载相关的依赖库,等下载成功后,再对 package 和下载的依赖进行一同的编译构建。

更新依赖:

ba
cargo update            # 更新所有依赖
cargo update -p regex   # 只更新 “regex”

清理cargo缓存

bash
cargo clean
cargo build #重新构建

变量可变性

使用mut关键字声明

rust
let mut x = 5;

使用下划线开头忽略未使用的变量

rust
fn main() {
    let _x = 5;
    let y = 10;
}

Cargo.toml 和 Cargo.lock

Cargo.tomlCargo.lockcargo 的核心文件,它的所有活动均基于此二者。

  • Cargo.tomlcargo 特有的项目数据描述文件。它存储了项目的所有元配置信息,如果 Rust 开发者希望 Rust 项目能够按照期望的方式进行构建、测试和运行,那么,必须按照合理的方式构建 Cargo.toml
  • Cargo.lock 文件是 cargo 工具根据同一项目的 toml 文件生成的项目依赖详细清单,因此我们一般不用修改它,只需要对着 Cargo.toml 文件撸就行了。

内置宏

Rust 还提供了一些内置宏,它们简化了常见的编程任务:

  • vec![]: 创建一个 Vec<T> 向量。
  • format!(): 格式化字符串。
  • println!(): 打印到标准输出。
  • assert!(): 断言条件为真。
  • panic!(): 引发 panic。
  • include!(): 包含另一个文件的内容。
  • concat!(): 连接常量字符串。
  • env!(): 获取环境变量。
  • dbg!() : debug 输出

可变引用

同一作用域,特定数据只能有一个可变引用
可变引用与不可变引用不能同时存在

字符串切片:是按字节算,一个中文算三个字节

Rust 中的字符是 Unicode 类型,因此每个字符占据 4 个字节内存空间,但是在字符串中不一样,字符串是 UTF-8 编码,也就是字符串中的字符所占的字节数是变化的(1 - 4)

String类型

在 Rust 中,String 是一个动态大小、可增长的字符串类型

  1. 内存管理:
  • String 是堆分配的,拥有自己的内存空间
  • 可以动态增长、修改
  • String 超出作用域时,会自动释放内存
  1. 特点:
  • 使用 UTF-8 编码
  • 可以通过 .push_str().push() 等方法修改
  • 拥有所有权,可以转移所有权
  • 可以通过 .clone() 深拷贝
  1. 创建方式:
rust
// 从字面量创建
let s1 = String::from("hello");
let s2 = "hello".to_string();

// 创建空字符串
let mut s3 = String::new();
  1. 常用方法:
  • .len(): 获取字节长度

  • .push_str(): 追加字符串

  • .push(): 追加单个字符

  • .pop(): 移除最后一个字符

  • .insert(): 插入单个字符 char

  • .insert_str(): 插入字符串字面量

    rust
    fn main() {
        let mut s = String::from("Hello rust!");
        s.insert(5, ',');
        println!("插入字符 insert() -> {}", s);
        s.insert_str(6, " I like");
        println!("插入字符串 insert_str() -> {}", s);
    }
  1. &str 的转换:
rust
let s: String = "hello".to_string();
let slice: &str = &s;  // 借用

主要用途是需要拥有和修改字符串的场景,而 &str 更适合只读引用。

String 类型转为 &str 类型

rust

fn main() {
    let s = String::from("hello,world!");
    say_hello(&s);
    say_hello(&s[..]);
    say_hello(s.as_str());
}

fn say_hello(s: &str) {
    println!("{}",s);
}

实际上这种灵活用法是因为 deref 隐式强制转换,具体我们会在 Deref 特征进行详细讲解。

连接 (Concatenate)

1、使用 + 或者 += 连接字符串

使用 + 或者 += 连接字符串,要求右边的参数必须为字符串的切片引用(Slice)类型。其实当调用 + 的操作符时,相当于调用了 std::string 标准库中的 add() 方法,这里 add() 方法的第二个参数是一个引用的类型。因此我们在使用 + 时, 必须传递切片引用类型。不能直接传递 String 类型。+ 是返回一个新的字符串,所以变量声明可以不需要 mut 关键字修饰

示例代码如下:

rust
fn main() {
    let string_append = String::from("hello ");
    let string_rust = String::from("rust");
    // &string_rust会自动解引用为&str
    let result = string_append + &string_rust;
    let mut result = result + "!"; // `result + "!"` 中的 `result` 是不可变的
    result += "!!!";

    println!("连接字符串 + -> {}", result);
}

代码运行结果:

console
连接字符串 + -> hello rust!!!!

引用和借用

在 Rust 中,引用(Reference)和借用(Borrowing)是密切相关的概念,但并不完全相同:

  1. 借用(Borrowing)是一种概念:
  • 指通过引用访问数据,而不获取其所有权
  • 分为可变借用(&mut)和不可变借用(&
  • 是 Rust 所有权系统的核心机制
  1. 引用(Reference)是具体的语法实现:
  • 使用 & 符号创建
  • 允许你引用某个值,但不获取其所有权

示例代码:

rust
fn main() {
    let x = 5;
    let y = &x;  // y 是 x 的引用,这里也是一次借用

    let mut s = String::from("hello");
    let r1 = &s;        // 不可变借用
    let r2 = &s;        // 可以同时有多个不可变借用
    let r3 = &mut s;    // 可变借用(互斥)
}

借用的规则:

  • 在同一作用域中,要么有一个可变引用,要么有多个不可变引用
  • 引用必须总是有效的

总之,借用是一个概念,而引用是实现这个概念的具体语法机制。

可变引用同时只能存在一个不过可变引用并不是随心所欲、想用就用的,它有一个很大的限制: 同一作用域,特定数据只能有一个可变引用

可变引用与不可变引用不能同时存在

悬垂引用(Dangling References)

悬垂引用也叫做悬垂指针,意思为指针指向某个值后,这个值被释放掉了,而指针仍然存在,其指向的内存可能不存在任何值或已被其它变量重新使用。在 Rust 中编译器可以确保引用永远也不会变成悬垂状态:当你获取数据的引用后,编译器可以确保数据不会在引用结束前被释放,要想释放数据,必须先停止其引用的使用。

让我们尝试创建一个悬垂引用,Rust 会抛出一个编译时错误

为什么&str 是字符串切片类型

&str 被称为字符串切片(string slice)主要是因为它的内存表示和行为特征:

  1. 内存结构
rust
// &str 本质上是一个胖指针(fat pointer),包含两部分:
// 1. 指向字符串数据的指针
// 2. 字符串的长度
struct StringSlice {
    ptr: *const u8,  // 指向字符串起始位置的指针
    len: usize,      // 字符串长度
}
  1. 切片特性
rust
let s = "hello world";
let part1: &str = &s[0..5];  // 获取 "hello"
let part2: &str = &s[6..];   // 获取 "world"
  1. 特点解释
  • 可以从原始字符串创建部分引用
  • 不拷贝数据,只是引用原始内存
  • 长度和起始位置是确定的
  1. 与数组切片类似
rust
let arr = [1, 2, 3, 4, 5];
let slice = &arr[1..3];  // 类比字符串切片
  1. 为什么叫"切片"
  • 可以从原始字符串"切"出一部分
  • 保留原始数据的引用
  • 不创建新的内存空间

示例:

rust
fn main() {
    let s = String::from("hello world");
    let hello: &str = &s[0..5];  // 切片引用
    let world: &str = &s[6..];   // 另一个切片
}

切片本质上是对连续内存区间的引用,提供了一种高效、零开销的部分访问方式。

str和&str的关系

在 Rust 中,str&str 的关系是这样的:

  1. str(字符串类型):
    • 是一个没有大小的动态大小类型(DST, Dynamically Sized Type)
    • 本身不能直接使用,总是需要通过引用来使用
    • 代表一个 UTF-8 编码的字符串序列
  2. &str(字符串切片):
    • str 类型的引用
    • 实际上是对 str 的借用
    • 包含两个部分: a. 指向字符串数据的指针 b. 字符串的长度

示例代码解释:

rust
// 字面量是 &str 类型
let s: &str = "hello world";

// 不能直接使用 str 类型
// let invalid: str = "this won't work"; // 编译错误

// 总是需要使用引用
fn takes_slice(slice: &str) {
    println!("{}", slice);
}

关键点:

  • str 本身是不完整的类型,只能通过 &str 来使用
  • &str 是最常见的字符串类型,用于字符串字面量和借用
  • 当你需要一个可以修改的字符串时,使用 String

这种设计反映了 Rust 对内存安全和所有权的严格控制,确保在编译时就能捕获潜在的内存使用问题。