02 Hello, world
2025/6/4大约 4 分钟
认识Cargo
cargo
是Rust 的构建系统和包管理器,类似于node的 npm
,python的 pip
,但是这个非常强,可以说是各个包管理器的集大成者,再挑剔的开发者都对它赞不绝口。
创建项目
cargo new world_hello
创建后的项目目录如下:
world_hello/
├── Cargo.toml # 项目配置文件
└── src/
└── main.rs # 项目入口文件
笔者的 cargo
版本是 1.87.0
,创建的项目并不存在 .gitignore
,我找到了老版本的 cargo 创建的,如下:
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# Generated by cargo mutants
# Contains mutation testing data
**/mutants.out*/
# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
运行项目
直接运行
cargo run
手动编译和运行
# 跟 C++ 挺像的吧
cargo build
./target/debug/world_hello
我们注意到了明晃晃的 debug
模式,这个模式下编译会非常快,但是运行速度有所下降,因为 debug
下 Rust 编译器不会做任何优化,只是为了尽快编译完成,那我们如何指定 release
模式呢,其实和其他语言一样,只需要在命令后面加上 --release
即可。
cargo run --release
cargo build --release
cargo check
项目一旦大了,编译时间就会变长,此时 cargo check
就闪亮登场了,这个命令只是检查一下当前代码编译是否可以通过,而不会生成可执行文件,因此这个命令执行非常的快。
cargo check
Cargo.toml 和 Cargo.lock
Cargo.toml
和 Cargo.lock
是cargo的核心文件,它的所有活动均基于此二者。
Cargo.toml
是cargo特有的项目数据描述文件。它存储了项目的所有元配置信息,如果你希望Rust项目能够按照期望的方式进行构建、测试和运行,那么,必须按照合理的方式构建Cargo.toml
。Cargo.lock
文件是cargo工具根据同一项目的toml文件生成的项目依赖详细清单,因此我们一般不用修改它,只需要对着Cargo.toml
文件修改就行了。
如果项目是一个可运行的程序,那么就需要上传
cargo.lock
,否则在.gitignore
中加上它。
接下来看看toml的具体内容:
[package]
name = "world_hello" # 项目名称
version = "0.1.0" # 项目版本,默认是0.1.0
edition = "2024" # 项目使用的Rust版本
包管理的最大优势就是在我们定义第三方依赖时非常方便的管理,Cargo.toml
更是如此,支持各种方式的引入,如下:
[dependencies]
rand = "0.3"
hammer = { version = "0.5.0"}
color = { git = "https://github.com/bjz/color-rs" }
geometry = { path = "crates/geometry" }
至此,我们应该对rust项目的创建有了初步的认知,接下来让我们一起修改一些代码吧~
多语言版本的Hello, world
fn greet_world() {
let southern_germany = "Grüß Gott!";
let chinese = "世界,你好!";
let english = "World, hello!";
let regions = [southern_germany, chinese, english];
for region in regions {
println!("{}", ®ion) // 此处的!是一种特殊的宏运算符号,这里不用关注
}
}
看到这段代码,可以说是非常熟悉,ts的遍历写法和C++23的std::print,以及常见的结构化绑定,都在这段代码有体现。
初见 Rust
直接上压力:
fn main() {
let penguin_data = "\
common name,length (cm)
Little penguin,33
Yellow-eyed penguin,65
Fiordland penguin,60
Invalid,data
";
let records = penguin_data.lines();
for (i, record) in records.enumerate() {
if i == 0 || record.trim().len() == 0 {
continue;
}
// 声明一个 `fields` 变量,类型是 `Vec`
// `Vec` 是 `vector` 的缩写,是一个可伸缩的集合类型,可以认为是一个动态数组
// `<_>` 表示 `Vec` 中的元素类型由编译器自行推断,在很多场景下,都会帮我们省却不少功夫
let fields: Vec<_> = record.split(',').map(|field| field.trim()).collect();
if cfg!(debug_assertions) {
// 输出到标准错误输出
eprintln!("debug: {:?} -> {:?}", record, fields);
}
let name = fields[0];
// 1. 尝试把 `fields[1]` 的值转换为 `f32` 类型的浮点数,如果成功,则把 `f32` 值赋给 `length` 变量
//
// 2. `if let` 是一个匹配表达式,用来从 = 右边的结果中,匹配出 `length` 的值:
// 1)当 = 右边的表达式执行成功,则会返回一个 `Ok(f32)` 的类型,若失败,则会返回一个 `Err(e)` 类型,`if let` 的作用就是仅匹配 `Ok` 也就是成功的情况,如果是错误,就直接忽略
// 2)同时 `if let` 还会做一次解构匹配,通过 `Ok(length)` 去匹配右边的 `Ok(f32)`,最终把相应的 `f32` 值赋给 `length`
//
// 3. 当然你也可以忽略成功的情况,用 `if let Err(e) = fields[1].parse::<f32>() {...}` 匹配出错误,然后打印出来,但是没啥卵用
if let Ok(length) = fields[1].parse::<f32>() {
// 输出到标准输出
println!("{}, {}cm", name, length);
}
}
}
看到上面的代码,是不是知道rust大概是个什么难度了吧,但是无需担心,攻克难关正是我们的乐趣所在,而且,你都学rust了,还怕什么难啊(乐)。
本节代码详见此处。