参考资料:https://course.rs/first-try/installation.html
1.1 安装 Rust 环境
(太简单,看原网页)
1.2 强推vscode
如果你的电脑慢,有一点一定要注意:
在编译器构建代码的同时,不要在终端再运行 cargo run
等命令进行编译,不然会获得一个报错提示,大意是当前文件目录已经被锁定,等待其它使用者释放。如果等了很久 IDE 还是没有释放(虽然我没遇到过,但是存在这个可能性),你可以关掉 IDE,并手动 kill
掉 rust-analyzer
,然后重新尝试。
1.3 认识 Cargo
总而言之,cargo
提供了一系列的工具,从项目的建立、构建到测试、运行直至部署,为 Rust 项目的管理提供尽可能完整的手段。同时,与 Rust 语言及其编译器 rustc
紧密结合,可以说用了后就忘不掉,如同初恋般的感觉。
创建一个"你好,世界"项目
终于到了紧张刺激的 new new new 环节:
$ cargo new world_hello
$ cd world_hello
上面的命令使用 cargo new
创建一个项目,项目名是 world_hello
(向读者势力低头的项目名称,泪奔),该项目的结构和配置文件都是由 cargo
生成,意味着我们的项目被 cargo
所管理。
早期的 cargo
在创建项目时,必须添加 --bin
的参数,如下所示:
$ cargo new world_hello --bin
$ cd world_hello
现在的版本,已经无需此参数,cargo
默认就创建 bin
类型的项目,
顺便说一句,Rust 项目主要分为两个类型:bin
和 lib
,前者是一个可运行的项目,后者是一个依赖库项目。
下面来看看创建的项目结构:
$ tree
.
├── .git
├── .gitignore
├── Cargo.toml
└── src
└── main.rs
是的,连 git
都给你创建了,不仅令人感叹,不是女儿,胜似女儿,比小棉袄还体贴。
有两种方式可以运行项目:
-
cargo run
-
手动编译和运行项目
首先来看看第一种方式,一码胜似千言,在之前创建的 world_hello
目录下运行:
$ cargo run
Compiling world_hello v0.1.0 (/Users/sunfei/development/rust/world_hello)
Finished dev [unoptimized + debuginfo] target(s) in 0.43s
Running `target/debug/world_hello`
Hello, world!
好了,你已经看到程序的输出:"Hello, world"
。
上述代码,cargo run
首先对项目进行编译,然后再运行,因此它实际上等同于运行了两个指令,下面我们手动试一下编译和运行项目:
编译
$ cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
运行
$ ./target/debug/world_hello
Hello, world!
行云流水,但谈不上一气呵成。 细心的读者可能已经发现,在调用的时候,路径 ./target/debug/world_hello
中有一个明晃晃的 debug
字段,没错我们运行的是 debug
模式,在这种模式下,代码的编译速度会非常快,可是福兮祸所依,运行速度就慢了. 原因是,在 debug
模式下,Rust 编译器不会做任何的优化,只为了尽快的编译完成,让你的开发流程更加顺畅。
作为尊贵的读者,咱自然可以要求更多,比如你想要高性能的代码怎么办? 简单,添加 --release
来编译:
cargo run --release
cargo build --release
试着运行一下我们高性能的 release
程序:
$ ./target/release/world_hello
Hello, world!
cargo check
当项目大了后,cargo run
和 cargo build
不可避免的会变慢,那么有没有更快的方式来验证代码的正确性呢?大杀器来了,接着!
cargo check
是我们在代码开发过程中最常用的命令,它的作用很简单:快速的检查一下代码能否编译通过。因此该命令速度会非常快,能节省大量的编译时间。
$ cargo check
Checking world_hello v0.1.0 (/Users/sunfei/development/rust/world_hello)
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
Rust 虽然编译速度还行,但是还是不能与 Go 语言相提并论,因为 Rust 需要做很多复杂的编译优化和语言特性解析,甚至连如何优化编译速度都成了一门学问: 优化编译速度
Cargo.toml 和 Cargo.lock
Cargo.toml
和 Cargo.lock
是 cargo
的核心文件,它的所有活动均基于此二者。
-
Cargo.toml
是cargo
特有的项目数据描述文件。它存储了项目的所有元配置信息,如果 Rust 开发者希望 Rust 项目能够按照期望的方式进行构建、测试和运行,那么,必须按照合理的方式构建Cargo.toml
。 -
Cargo.lock
文件是cargo
工具根据同一项目的toml
文件生成的项目依赖详细清单,因此我们一般不用修改它,只需要对着Cargo.toml
文件撸就行了。
什么情况下该把
Cargo.lock
上传到 git 仓库里?很简单,当你的项目是一个可运行的程序时,就上传Cargo.lock
,如果是一个依赖库项目,那么请把它添加到.gitignore
中
现在用 VSCode 打开上面创建的"世界,你好"项目,然后进入根目录的 Cargo.toml
文件,可以看到该文件包含不少信息:
package 配置段落
package
中记录了项目的描述信息,典型的如下:
[package]
name = "world_hello"
version = "0.1.0"
edition = "2021"
name
字段定义了项目名称,version
字段定义当前版本,新项目默认是 0.1.0
,edition
字段定义了我们使用的 Rust 大版本。因为本书很新(不仅仅是现在新,未来也将及时修订,跟得上 Rust 的小步伐),所以使用的是 Rust edition 2021
大版本,详情见 Rust 版本详解
定义项目依赖
使用 cargo
工具的最大优势就在于,能够对该项目的各种依赖项进行方便、统一和灵活的管理。
在 Cargo.toml
中,主要通过各种依赖段落来描述该项目的各种依赖项:
- 基于 Rust 官方仓库
crates.io
,通过版本说明来描述 - 基于项目源代码的 git 仓库地址,通过 URL 来描述
- 基于本地项目的绝对路径或者相对路径,通过类 Unix 模式的路径来描述
这三种形式具体写法如下:
[dependencies]
rand = "0.3"
hammer = { version = "0.5.0"}
color = { git = "https://github.com/bjz/color-rs" }
geometry = { path = "crates/geometry" }
相信聪明的读者已经能看懂该如何引入外部依赖库,这里就不再赘述。详细的说明参见此章:Cargo 依赖管理,但是不建议大家现在去看,只要按照目录浏览,拨云见雾指日可待。
1.4 不仅仅是 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.iter() {
println!("{}", ®ion);
}
}
fn main() {
greet_world();
}
打开终端,进入 world_hello
工程根目录,运行该程序。(你也可以在 VSCode 中打开终端,方法是点击 VSCode 上方菜单栏中的终端->新建终端,或者直接使用快捷键打开。)
上面的 So Easy
的余音仍在绕梁,我希望它能继续下去,可是… 人总是要面对现实,因此让我们来点狠活:
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 特性有:
- 控制流:
for
和continue
连在一起使用,实现循环控制。 - 方法语法:由于 Rust 没有继承,因此 Rust 不是传统意义上的面向对象语言,但是它却从
OO
语言那里偷师了方法的使用record.trim()
,record.split(',')
等。 - 高阶函数编程:函数可以作为参数也能作为返回值,例如
.map(|field| field.trim())
,这里map
方法中使用闭包函数作为参数,也可以称呼为匿名函数
、lambda 函数
。 - 类型标注:
if let Ok(length) = fields[1].parse::<f32>()
,通过::<f32>
的使用,告诉编译器length
是一个f32
类型的浮点数。这种类型标注不是很常用,但是在编译器无法推断出你的数据类型时,就很有用了。 - 条件编译:
if cfg!(debug_assertions)
,说明紧跟其后的输出(打印)只在debug
模式下生效。 - 隐式返回:Rust 提供了
return
关键字用于函数返回,但是在很多时候,我们可以省略它。因为 Rust 是 基于表达式的语言。
在终端中运行上述代码时,会看到很多 debug: ...
的输出,上面有讲,这些都是 条件编译
的输出,那么该怎么消除掉这些输出呢?
读者大大普遍冰雪聪明,肯定已经想到:是的,在 认识 Cargo中,曾经介绍过 --release
参数,因为 cargo run
默认是运行 debug
模式。因此想要消灭那些 debug:
输出,需要更改为其它模式,其中最常用的模式就是 --release
也就是生产发布的模式。
具体运行代码就不给了,留给大家作为一个小练习,建议亲自动手尝试下。
1.5. 下载依赖很慢或卡住?
在目前,大家还不需要自己搭建的镜像下载服务,因此只需知道下载依赖库的地址是 crates.io,是由 Rust 官方搭建的镜像下载和管理服务。
但悲剧的是,它的默认镜像地址是在国外,这就导致了某些时候难免会遇到下载缓慢或者卡住的情况,下面我们一起来看看。
下载很慢
解决下载缓慢有两种方式:
- 开启命令行或者全局FQ 经常有同学反馈,我明明开启FQ了,但是下载依然还是很慢,无论是命令行中下载还是 VSCode 的 rust-analyzer 插件自动拉取。
事实上,FQ工具默认开启的仅仅是浏览器的FQ代理,对于命令行或者软件中的访问,并不会代理流量,因此这些访问还是通过正常网络进行的,自然会失败。
因此,大家需要做的是在你使用的FQ工具中 复制终端代理命令
或者开启全局FQ。由于每个FQ软件的使用方式不同,因此具体的还是需要自己研究下。以我使用的 ClashX
为例,点击 复制终端代理命令
后,会自动复制一些 export
文本,将这些文本复制到命令行终端中,执行一下,就可以自动完成代理了。
export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7891
- 修改 Rust 的下载镜像为国内的镜像地址 这个效果最直接,但是就是稍有些麻烦。
为了使用 crates.io
之外的注册服务,我们需要对 $HOME/.cargo/config.toml
($CARGO_HOME 下) 文件进行配置,添加新的服务提供商,有两种方式可以实现:增加新的镜像地址和覆盖默认的镜像地址。
这里推荐使用科大的注册服务来提升下载速度,以下注册服务的链接都是科大的
增加新的镜像地址
首先是在 crates.io
之外添加新的注册服务,在 $HOME/.cargo/config.toml
(如果文件不存在则手动创建一个)中添加以下内容:
[registries]
ustc = { index = "https://mirrors.ustc.edu.cn/crates.io-index/" }
这种方式只会新增一个新的镜像地址,因此在引入依赖的时候,需要指定该地址,例如在项目中引入 time
包,你需要在 Cargo.toml
中使用以下方式引入:
[dependencies]
time = { registry = "ustc" }
在重新配置后,初次构建可能要较久的时间,因为要下载更新 ustc
注册服务的索引文件,还挺大的...
覆盖默认的镜像地址
事实上,我们更推荐第二种方式,因为第一种方式在项目大了后,实在是很麻烦,全部修改后,万一以后不用这个镜像了,你又要全部修改成其它的。
而第二种方式,则不需要修改 Cargo.toml
文件,因为它是直接使用新注册服务来替代默认的 crates.io
。
在 $HOME/.cargo/config.toml
添加以下内容:
[source.crates-io]
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
首先,创建一个新的镜像源 [source.ustc]
,然后将默认的 crates-io
替换成新的镜像源: replace-with = 'ustc'
。
简单吧?只要这样配置后,以往需要去 crates.io
下载的包,会全部从科大的镜像地址下载,速度刷刷的.. 我的 300M 大刀( 宽带 )终于有了用武之地。
这里强烈推荐大家在学习完后面的基本章节后,看一下 Cargo 使用指南章节,对于你的 Rust 之旅会有莫大的帮助!
下载卡住
下载卡住其实就一个原因:下载太慢了。
根据经验来看,卡住不动往往发生在更新索引时。毕竟 Rust 的包越来越多,索引也越来越大,如果不使用国内镜像,卡住还蛮正常的,好在,我们也无需经常更新索引 :P
Blocking waiting for file lock on package cache
不过这里有一个坑,需要大家注意,如果你同时打开了 VSCODE 和命令行,然后修改了 Cargo.toml
,此时 VSCODE 的 rust-analyzer 插件会自动检测到依赖的变更,去下载新的依赖。
在 VSCODE 下载的过程中( 特别是更新索引,可能会耗时很久),假如你又在命令行中运行类似 cargo run
或者 cargo build
的命令,就会提示一行有些看不太懂的内容:
$ cargo build
Blocking waiting for file lock on package cache
Blocking waiting for file lock on package cache
其实这个报错就是因为 VSCODE 的下载太慢了,而且该下载构建还锁住了当前的项目,导致你无法在另一个地方再次进行构建。
解决办法也很简单:
- 增加下载速度,见前面内容
- 耐心等待持有锁的用户构建完成
- 强行停止正在构建的进程,例如杀掉 IDE 使用的 rust-analyzer 插件进程,然后删除
$HOME/.cargo/.package_cache
目录
1.6 避免从入门到放弃
仔细阅读编译错误
在一些编程语言中,你可能习惯了编译器给出的错误只要看前面(或后面)几行就行,大部分是不怎么用到的信息,总之编译器总感觉笨笨的。
但是 Rust 不是,它为我们提供了一个强大无比的编译器,而且会提示我们该如何修改代码以解决错误,简直就是一个优秀的老师!
因此在使用 Rust 过程中,如果错误你不知该如何解决,不妨仔细阅读下编译器或者 IDE 给出的错误提示,绝大多数时候,你都可以通过这些提示顺利的解决问题。
同时也不要忽略编译器给出的警告信息(warnings),因为里面包含了 cargo clippy
给出的 lint
提示,这些提示不仅仅包含代码风格,甚至包含了一些隐藏很深的错误!至于这些错误为何不是 error
形式出现,随着学习的深入,你将逐渐理解 Rust 的各种设计选择,包括这个问题。
不要强制自己使用其它编程语言的最佳实践来写 Rust
大多数其它编程语言适用的最佳实践在 Rust 中也可以很好的使用,但是 Rust 并不是一门专门的面向对象或者函数式语言,因此在使用自己喜欢的编程风格时,也要考虑遵循 Rust 应有的实践。
例如纯面向对象或纯函数式编程,在 Rust 中就并不是一个很好的选择。如果你有过 Go 语言的编程经验,相信能更加理解我这里想表达的含义。
不过大家也不用担心,在书中我们以专题的形式专门讲解 Rust 的最佳实践,看完后自然就明白了。
标签:Cargo,cargo,牛刀,toml,world,小试,hello,Rust From: https://www.cnblogs.com/yinhuachen/p/16964765.html