掘金 后端 ( ) • 2024-04-26 13:41

讲动人的故事,写懂人的代码

  • 故事梗概:
  • 在她所维护的老旧Java系统即将被淘汰的危机边缘,这位在编程中总想快速完事的女程序员,希望能转岗到公司内部使用Rust语言的新项目组,因此开始自学Rust;
  • 然而,在掌握了Rust编程知识之后,为了通过Rust项目组的技术面试,使得转岗成功而不至被裁员,她必须领会编程如何"快速"才能有真正的意义。

上回的故事里,我们的Java程序员赵可菲和C++程序员席双嘉,在Rust大神贾克强的指导下,一起掌握了rustup工具链的用法。

接下来,他们准备把Rust书里的Guessing Game的需求稍作修改,然后重新写一遍代码,这样就能更好地学习Rust的基础知识了。

coraliewissing_In_a_bright_and_spacious_meeting_room_three_youn_36b4018b-7338-4db5-8f83-1c9cf4ae7874-xiaohongshu.png

2.1 猜骰子冷热游戏需求

那么,该如何调整书中的Guessing Game游戏的需求,让它既好玩又可以让大家练习书中的知识点呢?

贾克强突然想起了公司新近部署的AI编程助手艾极思。他想,也许可以问问它。

于是,贾克强对艾极思说:“我希望你能根据下面《The Rust Programming Language》书中的游戏描述,帮我改编一下需求,设计一个新游戏。新游戏要趣味十足,而且要保留原游戏的关键行为,让我在重写代码的过程中,可以练习到书中的主要知识点。”

“书中原游戏的描述是这样的:这是个猜数字的游戏,首先,程序会生成一个1到100之间的随机整数。“

”然后,程序会提示玩家猜这个数。“

”玩家输入他们所猜的数字后,程序会告诉他们这个猜测是偏低还是偏高。如果猜对了,游戏就会打印一条祝贺信息然后结束。”

艾极思马上回复了:“根据《The Rust Programming Language》书中的原游戏,我们可以设计一个新游戏,既保留了原有的基本逻辑,也加入了一些有趣的元素和挑战。“

”这样你在练习编程的同时,也可以享受游戏的乐趣。以下是新游戏的设定:”

“新游戏叫做:猜骰子冷热(Dicey Temperatures)”

“游戏规则是这样的:”

“1 首先,程序会生成两个骰子的点数之和,这个数是2到12之间的随机整数。”

“2 然后,程序会提示玩家猜两个骰子的点数之和。”

“3 如果玩家第一次输入点数之和就答错了,程序就会提示‘第一次猜就错了哦’。”

“4 如果玩家第二次或之后输入点数之和也答错了,但这次猜测比上次更接近正确答案,程序就会提示‘更热了’。”

“5 如果离正确答案更远了,程序就会提示‘更冷了’。”

“6 如果距离正确答案的距离和上次一样,程序就会提示‘不冷不热’。”

“7 如果玩家猜对了,程序就会提示‘恭喜你,猜对了!’”

大家看到艾极思的回复,都觉得这个新游戏真的很好玩。

接下来,就可以开始新的项目了。

2.2 如何创建一个Rust新项目

赵可菲:“创建新项目真是小菜一碟。书里写的超详细的,直接运行cargo new diceytemperatures就搞定了。”

2.3 Rust语言的命名风格

贾克强指着赵可菲的屏幕说:“等等。项目名要用锈族的snake_case风格哈。”

“其实嘛,Rust在英文里就是铁锈的意思,所以我们国内的朋友们就直接叫Rust程序员为锈族啦。“

”而且你知道吗,国外的Rust程序员他们自己都爱叫自己甲壳族(Rustaceans),因为这个词跟甲壳生物Crustacean差不多嘛。“

”有一些外国的程序员朋友就把那个橙色的螃蟹Ferris当做Rust程序员的非官方吉祥物了(如图2.1)。“

f_2.1.svg.png

图2.1 有一些外国的程序员朋友就把那个橙色的螃蟹Ferris当做Rust程序员的非官方吉祥物

"锈族或者甲壳族,对于所有的变量名、方法名、函数名、项目名、包名和模块名,我们都喜欢用snake_case风格哟。只有类名,我们才会用PascalCase。"

"snake_case风格,很简单明了,就是所有的单词都是小写,用下划线连接起来。”

赵可菲:“哦,我可能需要一点时间来适应这个锈族的习惯。”

她一边说,一边把命令改成cargo new dicey_temperatures

 zkf@mbp  ~  cargo new dicey_temperatures
     Created binary (application) `dicey_temperatures` package

“在Java的世界里,给项目起名字,真没有什么硬性规定。”

席双嘉:“C++这边也是如此,没有定论。”

席双嘉告诉赵可菲:“咱们搞定新项目后,就用git提交一次,怎么样?每改一点点就提交一次,这样就能明显看出哪些文件变了。”

赵可菲回应:“这主意不错。不过,我可没那么有耐心。这个提交的事你来吧。”

“没问题。“席双嘉接过键盘,顺手就用git提交了代码。

赵可菲接着又输入了cargo run来启动程序,屏幕上出现了“Hello, world!”。

2.4 确保构建稳定可靠的Cargo.lock文件

“看!”席双嘉一边指着屏幕一边说,“终端窗口提示符的颜色,从绿变黄了。这就意味着代码在上次提交后有点变化。”

赵可菲:“但是我们只是运行了程序,代码应该没动呀。”

席双嘉敲了下git status -uall,这样就能显示出所有未被git跟踪的文件。

屏幕上出现了一个名叫Cargo.lock的文件。

 zkf@mbp  ~/dicey_temperatures  ↱ main  git status -uall
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	Cargo.lock

nothing added to commit but untracked files present (use "git add" to track)

席双嘉:“看,只要一运行cargo run,Cargo.lock文件就被自动创建出来了嘛。”

他随便点开了这个文件。

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "dicey_temperatures"
version = "0.1.0"

赵可菲:“嘿,这个文件需要提交到版本库吗?”

贾克强:“对的,Cargo.lock文件得提交到版本库,让我们的构建更稳定和可靠。”

“就像咱们程序员最怕的那种情况,明明在自己这儿代码运行得好好的,但怎么在测试环境就犯傻了。”

“许多时候,这就是因为开发环境和测试环境不一致。”

“也就是说,虽然构建的是同一份代码,但由于环境差异,开发环境能跑的包在测试环境再构建后就跑不通了。这两个包本身就不一样。”

“Cargo.lock文件就是为了解决这个问题。”

“当你运行 cargo build 时,Cargo 会查看一下 Cargo.toml 文件,看看哪个版本的依赖项最合适。”

“然后它会把这些版本写入 Cargo.lock 文件。一旦有了这个文件,Cargo 会忽略所有的更新,只参考这个文件。”

“除非你想更新。那就得用 cargo update。”

“这个机制就保证了我们构建的包,无论过多久或是谁去构建,都是一致的,保护我们的项目不被新版本的依赖项带来的问题影响。”

赵可菲:“但我们并没有运行cargo build命令呀。”

贾克强:“哈哈!你们刚才运行的cargo run命令呀。“

”它会先执行cargo build来编译你的项目。如果编译成功,cargo run接着就会运行编译后的二进制文件。”

贾克强话音未落,席双嘉已经把Cargo.lock文件提交到版本库了。

2.4.1 Java世界如何确保构建稳定可靠

赵可菲笑着说:“在Java的世界里,要实现类似Rust中Cargo.lock的功能,我们得靠Maven和Gradle这两大神器了。”

“Maven就是通过pom.xml文件来管理项目的依赖。“

”要锁定依赖版本,保证我们构建的东西能稳稳的运行,Maven通常会在<dependencyManagement>里头指定依赖的具体版本,或者用Maven Enforcer插件之类的外部工具。“

”虽然Maven没有直接类似于Cargo.lock的文件,但我们可以在pom.xml中明确所有版本,并利用<dependencyManagement>来锁定它们。”

“此外,Maven的发行版和快照机制,也能分别帮我们管理稳定构建和开发构建。”

“然后是Gradle,它通过build.gradle文件来配置依赖。”

“和Maven一样,Gradle原来并不会自动产生锁文件,不过我们可以通过依赖约束等策略来达到类似的效果。”

“从Gradle 4.8版本开始,它引入了依赖锁文件的概念,允许我们开发者明确锁定版本。”

“只要运行gradle dependencies --write-locks命令,Gradle就会生成一个锁文件,这个文件会固定依赖的版本,这在功能上就像Rust的Cargo.lock一样,保证了不同环境和时间下构建结果的一致性。”

2.4.2 C++世界如何确保构建稳定可靠

席双嘉:“嗨,你知道吗?在C++的世界里,我们也有类似Rust中的Cargo.lock机制,就是用Conan这个小工具。”

“Conan,这可是专门为C++量身打造的包管理器哦,它能帮我们处理所有的依赖和版本控制问题,让项目构建得稳稳当当。”

“用Conan的话,它会给我们生成一个叫做conan.lock的文件,这个玩意儿和Rust的Cargo.lock差不多。”

“这个conan.lock文件的作用就是把项目依赖的版本给锁定住,这样无论在哪个环境下构建,依赖都能保持一致。”

“这样一来,就能避免因为依赖版本不同,在开发、测试和生产环境中出现的那些麻烦事儿。”

“虽然CMake本身并没有内建的生成锁文件的功能,但它可以跟Conan这样的包管理器搭个档,通过Conan来管理依赖和版本,也就能间接实现锁定机制了。”

“在CMake的项目里,你可以在CMakeLists.txt文件中包含Conan的配置,然后通过链接Conan管理的库来构建应用程序。”

2.5 小结

两位程序员在Rust大神的带领下,决定给原有的Rust编程书籍中的"Guessing Game"游戏需求来点变化,重新操刀代码。

他们找了个AI编程小助手艾极思,把游戏需求改头换面,变成了一个新的、好玩儿的游戏“猜骰子冷热”。

这个新游戏不仅保留了原游戏的精髓,还加入了新的元素和挑战,让编程学习变得更加有趣。

他们用 cargo new 命令创了个新的Rust项目,还学习了Rust语言的命名风格。

命名风格 Rust/Python Java/Kotlin/Scala C/C++ Class Name PascalCase PascalCase PascalCase Method Name snake_case camelCase camelCase Variable Name snake_case camelCase snake_case Function Name snake_case - snake_case Project Name snake_case - - Package Name snake_case lowercase - Module Name snake_case - -

项目搞定后,他们咔嚓一下cargo run,程序就跑起来了。你会发现,跑这个命令会自动弄出个Cargo.lock文件。

这小文件可是厉害了,它能保证我们构建的稳定性和一致性呢。Java和C++的世界里,也有类似的东西,比如Java的Maven和Gradle,C++的Conan等等。

语言 机制 文件 特性 Rust Cargo Cargo.lock 锁定依赖版本。通过cargo build或cargo run自动创建和更新。 Java Maven/Gradle pom.xml/build.gradle 通过Maven的和Gradle的依赖约束来锁定依赖版本。Gradle 4.8+可以生成一个锁文件。 C++ Conan conan.lock 锁定依赖版本。与CMake一起管理依赖和版本。

如果你想要了解Rust是如何通过超越传统赋值语句的binding,实现不变性、模式匹配和所有权设计理念的,那就关注我,继续看下去吧!

【未完待续】


如果喜欢我的文章,期待你的点赞、在看和转发。

如果不喜欢,在评论区留个言告诉我哪里不喜欢呗~