Read Buf

Read Buf

Rust 和 Cargo 快速入门

这篇文章是一个 Rust 和 Cargo 的入门教程,在本教程中,我们将讨论以下内容:

  • 设置您的 IDE
  • 安装 Rust
  • 开始使用 Cargo
  • 为生产编译您的程序

如果您有任何反馈或希望我们讨论其他内容,请在 Twitter 上 @nbrempel 告诉我!

设置您的 IDE

有许多代码编辑器,我相信这些工具中的许多都是生产环境中的高效环境。然而,如果您不确定使用什么,我推荐使用 Visual Studio Code 和 Rust Analyzer 扩展。我使用这个是因为它有许多有用的工具——尤其是在理解 Rust 的所有权概念时。

安装 Rust

在 Rust 中工作的第一步是在您的机器上安装它。做这件事最好的方式是使用 rustup,一个易于使用的安装器。使用以下命令在您的机器上安装该工具:

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

按照这个提示中的指示安装您需要开始在 Rust 中工作的一切。一旦安装完成,您可以通过运行 rustup update 来轻松更新到 Rust 的最新版本。

使用 Cargo

Cargo 是 Rust 的构建工具和包管理器。它可以管理和发布 crates(这是 Rust 的外部库和依赖项所称的),创建新的 Rust 组件,并编译您的项目。

开始一个新项目

使用 Cargo 创建一个新的 Rust 项目非常简单。以下命令将创建一个名为 hello-world 的新项目:

> cargo new hello-world
Created binary (application) `hello-world` package

如果您已经有一个现有的目录想要使用,您可以使用 cargo init:

> mkdir existing-directory
> cd existing-directory/
> cargo init --name hello-world

Created binary (application) package

此时,您将拥有一个非常简单的 hello world 项目:

.
├── Cargo.toml
└── src
    └── main.rs
[package]
name = "hello-world"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

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

这是最基本的 Hello World 应用程序包,它只是输出字符串 “Hello, world!”。要运行此程序,您可以使用 Cargo 构建并执行二进制文件:

> cargo run
Compiling hello-world v0.1.0 (/Users/nremp/Projects/existing-directory)
    Finished dev [unoptimized + debuginfo] target(s) in 0.64s
     Running `target/debug/hello-world`
Hello, world!

您现在已经正式运行了您的第一个 Rust 程序!

Cargo 还会在 /src/bin 目录中寻找入口点。例如,您可以添加一个名为 /src/bin/hello-bin.rs 的文件,并像这样执行这个程序:

cargo run --bin hello-bin

lib 与 bin

在前面的例子中,我们使用命令 cargo new hello-world 创建了一个新的 Rust 项目。此命令创建了一个新的二进制包,编译后会产生一个旨在运行的可执行文件。实际上,我们可以传递 –bin 标志以更明确:

cargo new hello-world --bin

但是如果我们想创建一个旨在被其他项目导入的库怎么办?在这种情况下,我们可以传递 –lib 标志给 cargo new 来创建一个库包:

> cargo new my-lib --lib
Created library `my-lib` package

在这种情况下,Cargo 生成了不同的目录结构:

.
├── Cargo.toml
└── src
    └── lib.rs
pub fn add(left: usize, right: usize) -> usize {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        println!("test result: {result}");
        assert_eq!(result, 4);
    }
}

请注意,生成的示例中包括了一个单元测试。由于我们无法直接运行这个库,我们可以在代码旁边包含一个测试来查看它的工作情况。您可以使用命令 cargo test 来运行测试。专业提示:如果您向测试中添加任何打印语句,除非您使用 –nocapture 标志,否则您将看不到输出!您可以在这里阅读更多关于 Rust 的测试框架的信息。

> cargo test -- --nocapture
    Finished test [unoptimized + debuginfo] target(s) in 0.01s
     Running unittests src/lib.rs (target/debug/deps/my_lib-899502c70b06808d)

running 1 test
test result: 4
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests my-lib

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

综合起来

编写 Rust 应用程序的一个典型模式是从一个单一模块开始,只有在文件长度变得难以处理或当您想在多个入口点之间分享逻辑时才将您的工作拆分为多个文件。让我们看一个简单的例子。假设我们有以下 main.rs:

// 一个简单的程序,它接收一系列数字作为输入并打印这些数字的总和。
fn main() {
    let integers = std::env::args()
        .skip(1)
        .map(|x| x.parse::<i32>().unwrap())
        .collect::<Vec<i32>>();
    let sum = sum(&integers);

    println!("Sum of {integers:?} is {sum}");

    // 做其他事情...
}

fn sum(integers: &[i32]) -> i32 {
    integers.iter().fold(0, |sum, x| sum + x)
}

运行这个程序会产生以下输出:

> cargo run 1 2 3 4 5
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/hello-world 1 2 3 4 5`
Sum of [1, 2, 3, 4, 5] is 15

这显然是一个人为的例子。但如果出于某种原因,您想从另一个模块调用 sum,您还不能这样做。为了做到这一点,sum 需要被移到 lib.rs。我们最终得到的可能是以下情况:

.
├── Cargo.toml
└── src
    ├── bin
    │   └── sum.rs
    └── lib.rs
pub fn sum(integers: &[i32]) -> i32 {
    integers.iter().fold(0, |sum, x| sum + x)
}
use hello_world::sum;

fn main() {
    let integers = std::env::args()
        .skip(1)
        .map(|x| x.parse::<i32>().unwrap())
        .collect::<Vec<i32>>();
    let sum = sum(&integers);

    println!("Sum of {integers:?} is {sum}");

    // 做其他事情...
}

我们可以使用 use 关键字来导入 sum 函数。确保在库中使用 pub 关键字,这样它就可以从其他模块导入!

Workspaces

如果您的项目足够大,您可能会考虑将项目进一步拆分为更多组件,这就是所谓的 workspace。workspace 是 Cargo 使用的一种模式,允许您将相关代码隔离到项目目录中的本地 crates 中。每个 crate 都有自己的 Cargo.toml 文件,因此您可以控制编译器产生的二进制文件的大小。当处理许多文件时,workspaces 还有助于逻辑上的分离。

编译您的程序

当您执行 cargo run 时,您会注意到项目中出现了一个新的 target 目录。这是因为 Cargo 在运行输出之前隐式地将您的项目编译在调试模式下。如果您愿意,您可以直接运行结果。在我们之前的例子中,我们有一个 sum.rs 文件。这被编译成适用于您的机器架构的 sum 二进制文件:

> ./target/debug/sum 1 2 3 4 5
Sum of [1, 2, 3, 4, 5] is 15

默认的 dev 发布配置编译速度更快,但实际运行速度更慢。当您想要将您的工作部署到某个生产环境中时,您将希望使用发布配置来使您的程序速度提高 10-100 倍。

> cargo build --release
   Compiling hello-world v0.1.0 (/Users/nremp/Projects/hello-world)
    Finished release [optimized] target(s) in 0.76s

> ./target/release/sum 1 2 3 4 5
Sum of [1, 2, 3, 4, 5] is 15

这些发布配置是可配置的,但 dev 和 release 是一些合理的内置默认值。