Statistics
6
Views
0
Downloads
0
Donations
Support
Share
Uploader

高宏飞

Shared on 2026-06-04

AuthorSteve Klabnik,Carol Nichols,Rust 中文社区译

前言虽然不是那么明显,但Rust程序设计语言的本质在于赋能(empowerment):无论你现在编写的是 何种代码,Rust能让你在更为广泛的编程领域走得更远,写出自信。 比如,“系统层面”(“systems-level”)的工作,涉及内存管理、数据表示和并发等底层细节。从传统 角度来看,这是一个神秘的编程领域,只为浸润多年的极少数人所触及,也只有他们能避开那些臭名 昭著的陷阱。即使谨慎的实践者,亦唯恐代码出现漏洞、崩溃或损坏。 Rust破除了这些障碍,其消除了旧的陷阱并提供了伴你一路同行的友好、精良的工具。想要“深入” 底层控制的程序员可以使用Rust,无需冒着常见的崩溃或安全漏洞的风险,也无需学习时常改变的工 具链的最新知识。其语言本身更是被设计为自然而然的引导你编写出在运行速度和内存使用上都十分 高效的可靠代码。 已经在从事编写底层代码的程序员可以使用Rust来提升抱负。例如,在Rust中引入并行是相对低风 险的操作:编译器会为你捕获经典的错误。同时你可以自信的采取更为积极的优化,而不会意外引入 崩溃或漏洞。 但Rust并不局限于底层系统编程。其表现力和工效足以令人愉悦的编写出CLI应用、web server和 很多其他类型的代码——在本书中你会看到两个简单示例。使用Rust能将你在一个领域中学习的技 能延伸到另一个领域;你可以学习Rust来编写web应用,接着将同样的技能应用到你的Raspberry Pi(树莓派)上。 本书全面介绍了Rust为用户赋予的能力。其内容平易近人,致力于帮助你提升Rust的知识,并且提 升你作为程序员整体的理解与自信。那么让我们准备深入学习Rust吧(打开新世界的大门:))—— 欢迎加入Rust社区! — Nicholas Matsakis和Aaron Turon

Tags
No tags
Publisher: No Starch Press
Publish Year: 2018
Language: 英文
Pages: 525
File Format: PDF
File Size: 17.6 MB
Support Statistics
¥.00 · 0times
Text Preview (First 20 pages)
Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 1/525 Rust 程序设计语言 title-page.md commit 126aa9faa8bd67468495d164e54a566208db50e8 Steve Klabnik 和 Carol Nichols,以及来自 Rust 社区的贡献(Rust 中文社区翻译) 本书假设你使用 Rust 1.55.0 或更新的版本,且在所有的项目中的 Cargo.toml 文件中通过 edition="2018" 采用 Rust 2018 Edition 规范。请查看 第一章的 “安装” 部分 了解如何安装和升级 Rust,并查看新的 附录 E 了解版本相关的信息。 Rust 程序设计语言的 2018 Edition 包含许多的改进使得 Rust 更为工程化并更为容易学习。本书的此 次迭代包括了很多反映这些改进的修改: 第七章 “使用包、Crate 和模块管理不断增长的项目” 基本上被重写了。模块系统和路径 (path)的工作方式变得更为一致。 第十章新增了名为 “trait 作为参数” 和 “返回实现了 trait 的类型” 部分来解释新的 impl Trait 语法。 第十一章新增了一个名为 “在测试中使用 Result<T, E> ” 的部分来展示如何使用 ? 运算符来编 写测试 第十九章的 “高级生命周期” 部分被移除了,因为编译器的改进使得其内容变得更为少见。 之前的附录 D “宏” 得到了补充,包括了过程宏并移动到了第十九章的 “宏” 部分。 附录 A “关键字” 也介绍了新的原始标识符(raw identifiers)功能,这使得采用 2015 Edition 编写的 Rust 代码可以与 2018 Edition 互通。 现在的附录 D 名为 “实用开发工具”,它介绍了最近发布的可以帮助你编写 Rust 代码的工具。 我们还修复了全书中许多错误和不准确的描述。感谢报告了这些问题的读者们! 注意任何 “Rust 程序设计语言” 早期迭代中的代码在项目的 Cargo.toml 中不包含 edition="2018" 时 仍可以继续编译,哪怕你更新了 Rust 编译器的版本。Rust 的后向兼容性保证了这一切可以正常运 行! 本书的 HTML 版本可以在 https://doc.rust-lang.org/stable/book/ (简体中文译本)在线阅读,离线 版则包含在通过 rustup 安装的 Rust 中;运行 rustup docs --book 可以打开。 本书的 纸质版和电子书由 No Starch Press 发行。 前言
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 2/525 foreword.md commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f 虽然不是那么明显,但 Rust 程序设计语言的本质在于 赋能(empowerment):无论你现在编写的是 何种代码,Rust 能让你在更为广泛的编程领域走得更远,写出自信。 比如,“系统层面”(“systems-level”)的工作,涉及内存管理、数据表示和并发等底层细节。从传统 角度来看,这是一个神秘的编程领域,只为浸润多年的极少数人所触及,也只有他们能避开那些臭名 昭著的陷阱。即使谨慎的实践者,亦唯恐代码出现漏洞、崩溃或损坏。 Rust 破除了这些障碍,其消除了旧的陷阱并提供了伴你一路同行的友好、精良的工具。想要 “深入” 底层控制的程序员可以使用 Rust,无需冒着常见的崩溃或安全漏洞的风险,也无需学习时常改变的工 具链的最新知识。其语言本身更是被设计为自然而然的引导你编写出在运行速度和内存使用上都十分 高效的可靠代码。 已经在从事编写底层代码的程序员可以使用 Rust 来提升抱负。例如,在 Rust 中引入并行是相对低风 险的操作:编译器会为你捕获经典的错误。同时你可以自信的采取更为积极的优化,而不会意外引入 崩溃或漏洞。 但 Rust 并不局限于底层系统编程。其表现力和工效足以令人愉悦的编写出 CLI 应用、web server 和 很多其他类型的代码 —— 在本书中你会看到两个简单示例。使用 Rust 能将你在一个领域中学习的技 能延伸到另一个领域;你可以学习 Rust 来编写 web 应用,接着将同样的技能应用到你的 Raspberry Pi(树莓派)上。 本书全面介绍了 Rust 为用户赋予的能力。其内容平易近人,致力于帮助你提升 Rust 的知识,并且提 升你作为程序员整体的理解与自信。那么让我们准备深入学习 Rust 吧(打开新世界的大门 :)) —— 欢迎加入 Rust 社区! — Nicholas Matsakis 和 Aaron Turon 介绍 ch00-00-introduction.md commit 578591a3db98bc645aae4bcc2eed85db458a4e24 注意:本书的版本与出版的 The Rust Programming Language 和电子版的 No Starch Press 一致
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 3/525 欢迎阅读 “Rust 程序设计语言”,一本介绍 Rust 的书。Rust 程序设计语言能帮助你编写更快、更可靠 的软件。在编程语言设计中,高层工程学和底层控制往往不能兼得;Rust 则试图挑战这一矛盾。通过 权衡强大的技术能力与优秀的开发体验,Rust 允许你控制底层细节(比如内存使用),并免受以往进 行此类控制所经受的所有烦恼。 谁会使用 Rust Rust 因多种原因适用于很多开发者。让我们讨论几个最重要的群体。 开发者团队 Rust 被证明是可用于大型的、拥有不同层次系统编程知识的开发者团队间协作的高效工具。底层代码 中容易出现种种隐晦的 bug,在其他编程语言中,只能通过大量的测试和经验丰富的开发者细心的代 码评审来捕获它们。在 Rust 中,编译器充当了守门员的角色,它拒绝编译存在这些难以捕获的 bug 的代码,这其中包括并发 bug。通过与编译器合作,团队将更多的时间聚焦在程序逻辑上,而不是追 踪 bug。 Rust 也为系统编程世界带来了现代化的开发工具: Cargo,内置的依赖管理器和构建工具,它能轻松增加、编译和管理依赖,并使其在 Rust 生态 系统中保持一致。 Rustfmt 确保开发者遵循一致的代码风格。 Rust Language Server 为集成开发环境(IDE)提供了强大的代码补全和内联错误信息功能。 通过使用 Rust 生态系统中的这些和其他工具,开发者可以在编写系统层面代码时保持高生产力。 学生 Rust 适用于学生和有兴趣学习系统概念的人。通过 Rust,很多人已经了解了操作系统开发等主题。 社区非常欢迎和乐于解答学生们的问题。通过本书的努力,Rust 团队希望系统概念能被更多人了解, 特别是编程新手。 公司 数以百计的公司,无论规模大小,都在生产中使用Rust来完成各种任务。这些任务包括命令行工具、 web 服务、DevOps 工具、嵌入式设备、音视频分析与转码、加密货币(cryptocurrencies)、生物 信息学(bioinformatics)、搜索引擎、物联网(internet of things, IOT)程序、机器学习,甚至还 包括 Firefox 浏览器的大部分内容。
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 4/525 开源开发者 Rust 适用于希望构建 Rust 编程语言、社区、开发工具和库的开发者。我们很乐意您为 Rust 语言做 贡献。 重视速度和稳定性的开发者 Rust 适用于追求编程语言的速度与稳定性的开发者。所谓速度,是指你用 Rust 开发出的程序运行速 度,以及 Rust 提供的程序开发速度。Rust 的编译器检查确保了增加功能和重构代码时的稳定性。这 与缺少这些检查的语言形成鲜明对比,开发者通常害怕修改那些脆弱的遗留代码。力求零开销抽象 (zero-cost abstractions),把高级的特性编译成底层的代码,这样写起来很快,运行起来也很快, Rust 致力于使安全的代码也同样快速。 Rust 语言也希望能支持很多其他用户,这里提及的只是最大的利益相关者。总的来讲,Rust 最重要 的目标是消除数十年来程序员不得不做的权衡:安全 与 生产力,速度 与 人机交互的顺畅度 (ergonomics)。请尝试 Rust,看看这个选择是否适合你。 本书是写给谁的 本书假设你已经使用其他编程语言编写过代码,但并不假设你使用的是何种语言。我们尝试使这些参 考资料能广泛的适用于来自很多不同编程背景的开发者。我们不会花费很多时间讨论编程 是 什么或 者如何理解它。如果编程对于你来说是完全陌生的,你最好先阅读专门介绍编程的书籍。 如何阅读本书 总体来说,本书假设你会从头到尾顺序阅读。稍后的章节建立在之前章节概念的基础上,同时之前的 章节可能不会深入讨论某个主题的细节;通常稍后的章节会重新讨论这些主题。 你会在本书中发现两类章节:概念章节和项目章节。在概念章节中,我们学习 Rust 的某个方面。在 项目章节中,我们应用目前所学的知识一同构建小的程序。第二、十二和二十章是项目章节;其余都 是概念章节。 第一章介绍如何安装 Rust,如何编写 “Hello, world!” 程序,以及如何使用 Rust 的包管理器和构建工 具 Cargo。第二章是 Rust 语言的实战介绍。我们会站在较高的层次介绍一些概念,而将详细的介绍 放在稍后的章节中。如果你希望立刻就动手实践一下,第二章正好适合你。开始阅读时,你甚至可能 希望略过第三章,它介绍了 Rust 中类似其他编程语言中的功能,并直接阅读第四章学习 Rust 的所有 权系统。然而,如果你是特别重视细节的学习者,并倾向于在继续之前学习每一个细节,你可能希望 略过第二章并直接阅读第三章,并在想要构建项目来实践这些细节时再回来阅读第二章。
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 5/525 第五章讨论结构体和方法,第六章介绍枚举、 match 表达式和 if let 控制流结构。在 Rust 中,你 将使用结构体和枚举创建自定义类型。 第七章你会学习 Rust 的模块系统和私有性规则来组织代码和公有应用程序接口(Application Programming Interface, API)。第八章讨论了一些标准库提供的常见集合数据结构,比如 可变长数 组(vector)、字符串和哈希 map。第九章探索了 Rust 的错误处理哲学和技术。 第十章深入介绍泛型、trait 和生命周期,他们提供了定义出适用于多种类型的代码的能力。第十一章 全部关于测试,即使 Rust 有安全保证,也需要测试确保程序逻辑正确。第十二章,我们构建了属于 自己的在文件中搜索文本的命令行工具 grep 的子集功能实现。为此会利用之前章节讨论的很多概 念。 第十三章探索了闭包和迭代器:Rust 中来自函数式编程语言的功能。第十四章会更深层次的理解 Cargo 并讨论向他人分享库的最佳实践。第十五章讨论标准库提供的智能指针以及启用这些功能的 trait。 第十六章会学习不同的并发编程模型,并讨论 Rust 如何助你无畏的编写多线程程序。第十七章着眼 于比较 Rust 风格与你可能熟悉的面向对象编程原则。 第十八章是关于模式和模式匹配的参考章节,它是在Rust程序中表达思想的有效方式。第十九章是一 个高级主题大杂烩,包括 unsafe Rust、宏和更多关于生命周期、 trait、类型、函数和闭包的内容。 第二十章将会完成一个项目,我们会实现一个底层的、多线程的 web server! 最后是一些附录,包含了一些关于语言的参考风格格式的实用信息。附录 A 介绍了 Rust 的关键字。 附录 B 介绍 Rust 的运算符和符号。附录 C 介绍标准库提供的派生 trait。附录 D 涉及了一些有用的开 发工具,附录 E 介绍了 Rust 的不同版本。 怎样阅读本书都不会有任何问题:如果你希望略过一些内容,请继续!如果你发现疑惑可能会再跳回 之前的章节。请随意阅读。 学习 Rust 的过程中一个重要的部分是学习如何阅读编译器提供的错误信息:它们会指导你编写出能 工作的代码。为此,我们会提供很多不能编译的示例,以及各个情况下编译器会展示的错误信息。请 注意如果随便输入并运行随机的示例代码,它们可能无法编译!请确保阅读任何你尝试运行的示例周 围的内容,检视他们是否有意写错。Ferris 也会帮助你区别那些有意无法工作的代码: Ferris 意义 这些代码不能编译! 这些代码会 panic!
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 6/525 Ferris 意义 这些代码不会产生期望的行为。 在大部分情况,我们会指引你将任何不能编译的代码纠正为正确版本。 源代码 生成本书的源码可以在 GitHub 上找到。 译者注:本译本的 GitHub 仓库,欢迎 Issue 和 PR :) 入门指南 ch01-00-getting-started.md commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f 让我们开始 Rust 之旅!有很多内容需要学习,但每次旅程总有起点。在本章中,我们会讨论: 在 Linux、macOS 和 Windows 上安装 Rust 编写一个打印 Hello, world! 的程序 使用 Rust 的包管理器和构建系统 cargo 安装 ch01-01-installation.md commit aa6f28089364b148bbc6baddd59a2625dcc4dfba 第一步是安装 Rust。我们会通过 rustup 下载 Rust,这是一个管理 Rust 版本和相关工具的命令行 工具。下载时需要联网。
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 7/525 注意:如果你出于某些理由倾向于不使用 rustup,请参阅 Rust 的其他安装方法页面 了解更多 选项。 接下来的步骤会安装最新的稳定版 Rust 编译器。Rust 的稳定性确保本书所有示例在最新版本的 Rust 中能够继续编译。不同版本的输出可能略有不同,因为 Rust 经常改进错误信息和警告。也就是说, 任何通过这些步骤安装的最新稳定版 Rust,都应该能正常运行本书中的内容。 命令行标记 本章和全书中,我们会展示一些在终端中使用的命令。所有需要输入到终端的行都以 $ 开头。 但无需输入 $;它代表每行命令的起点。不以 $ 起始的行通常展示之前命令的输出。另外, PowerShell 专用的示例会采用 > 而不是 $。 在 Linux 或 macOS 上安装 rustup 如果你使用 Linux 或 macOS,打开终端并输入如下命令: 此命令下载一个脚本并开始安装 rustup 工具,这会安装最新稳定版 Rust。过程中可能会提示你输 入密码。如果安装成功,将会出现如下内容: 另外,你还需要一个链接器(linker),这是 Rust 用来将其编译的输出连接到一个文件中的程序。很 可能你已经有一个了。如果你遇到了链接器错误,请尝试安装一个 C 编译器,它通常包括一个链接 器。C 编译器也很有用,因为一些常见的 Rust 包依赖于 C 代码,因此需要安装一个 C 编译器。 在 macOS 上,你可以通过运行以下命令获得 C 语言编译器: 根据发行版的文档,Linux 用户通常应该安装 GCC 或 Clang。例如,如果你使用 Ubuntu,则可以安 装 build-essential 包。 在 Windows 上安装 rustup $ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh Rust is installed now. Great! $ xcode-select --install
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 8/525 在 Windows 上,前往 https://www.rust-lang.org/install.html 并按照说明安装 Rust。在安装过程的 某个步骤,你会收到一个信息说明为什么需要安装 Visual Studio 2013 或更新版本的 C++ build tools。获取这些 build tools 最方便的方法是安装 Build Tools for Visual Studio 2019。当被问及需 要安装什么的时候请确保选择 ”C++ build tools“,并确保包括了 Windows 10 SDK 和英文语言包 (English language pack)组件。 本书的余下部分会使用能同时运行于 cmd.exe 和 PowerShell 的命令。如果存在特定差异,我们会解 释使用哪一个。 更新和卸载 通过 rustup 安装了 Rust 之后,很容易更新到最新版本。在 shell 中运行如下更新脚本: 为了卸载 Rust 和 rustup,在 shell 中运行如下卸载脚本: 故障排除(Troubleshooting) 要检查是否正确安装了 Rust,打开 shell 并运行如下行: 你应能看到已发布的最新稳定版的版本号、提交哈希和提交日期,显示为如下格式: 如果出现这些内容,Rust 就安装成功了!如果并没有看到这些信息,并且使用的是 Windows,请检 查 Rust 是否位于 %PATH% 系统变量中。如果一切正确但 Rust 仍不能使用,有许多地方可以求助。最 简单的是 位于 Rust 官方 Discord 上的 #beginners 频道。在这里你可以和其他 Rustacean(Rust 用 户的称号,有自嘲意味)聊天并寻求帮助。其它给力的资源包括用户论坛和 Stack Overflow。 译者:恭喜入坑!(此处应该有掌声!) 本地文档 $ rustup update $ rustup self uninstall $ rustc --version rustc x.y.z (abcabcabc yyyy-mm-dd)
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 9/525 安装程序也自带一份文档的本地拷贝,可以离线阅读。运行 rustup doc 在浏览器中查看本地文档。 任何时候,如果你拿不准标准库中的类型或函数的用途和用法,请查阅应用程序接口(application programming interface,API)文档! Hello, World! ch01-02-hello-world.md commit 9c0fa2714859738ff73cbbb829592e4c037d7e46 既然安装好了 Rust,我们来编写第一个 Rust 程序。当学习一门新语言的时候,使用该语言在屏幕上 打印 Hello, world! 是一项传统,我们将沿用这一传统! 注意:本书假设你熟悉基本的命令行操作。Rust 对于你的编辑器、工具,以及代码位于何处并 没有特定的要求,如果你更倾向于使用集成开发环境(IDE),而不是命令行,请尽管使用你喜 欢的 IDE。目前很多 IDE 已经不同程度的支持 Rust;查看 IDE 文档了解更多细节。最近,Rust 团队已经致力于提供强大的 IDE 支持,而且进展飞速! 创建项目目录 首先创建一个存放 Rust 代码的目录。Rust 并不关心代码的存放位置,不过对于本书的练习和项目来 说,我们建议你在 home 目录中创建 projects 目录,并将你的所有项目存放在这里。 打开终端并输入如下命令创建 projects 目录,并在 projects 目录中为 “Hello, world!” 项目创建一个目 录。 对于 Linux、macOS 和 Windows PowerShell,输入: 对于 Windows CMD,输入: $ mkdir ~/projects $ cd ~/projects $ mkdir hello_world $ cd hello_world > mkdir "%USERPROFILE%\projects" > cd /d "%USERPROFILE%\projects" > mkdir hello_world > cd hello_world
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 10/525 编写并运行 Rust 程序 接下来,新建一个源文件,命名为 main.rs。Rust 源文件总是以 .rs 扩展名结尾。如果文件名包含多 个单词,使用下划线分隔它们。例如命名为 hello_world.rs,而不是 helloworld.rs。 现在打开刚创建的 main.rs 文件,输入示例 1-1 中的代码。 文件名: main.rs 示例 1-1: 一个打印 Hello, world! 的程序 保存文件,并回到终端窗口。在 Linux 或 macOS 上,输入如下命令,编译并运行文件: 在 Windows 上,输入命令 .\main.exe,而不是 ./main: 不管使用何种操作系统,终端应该打印字符串 Hello, world!。如果没有看到这些输出,回到安装 部分的 “故障排除” 小节查找有帮助的方法。 如果 Hello, world! 出现了,恭喜你!你已经正式编写了一个 Rust 程序。现在你成为一名 Rust 程 序员,欢迎! 分析这个 Rust 程序 现在,让我们回过头来仔细看看 “Hello, world!” 程序中到底发生了什么。这是第一块拼图: 这几行定义了一个 Rust 函数。 main 函数是一个特殊的函数:在可执行的 Rust 程序中,它总是最先 运行的代码。第一行代码声明了一个叫做 main 的函数,它没有参数也没有返回值。如果有参数的 话,它们的名称应该出现在小括号中, ()。 fn main() { println!("Hello, world!"); } $ rustc main.rs $ ./main Hello, world! > rustc main.rs > .\main.exe Hello, world! fn main() { }
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 11/525 还须注意,函数体被包裹在花括号中, {}。Rust 要求所有函数体都要用花括号包裹起来。一般来 说,将左花括号与函数声明置于同一行并以空格分隔,是良好的代码风格。 如果你希望在 Rust 项目中保持一种标准风格,可以使用名为 rustfmt 的自动格式化工具将代码格式 化为特定的风格。Rust 团队已经在标准的 Rust 发行版中包含了这个工具,就像 rustc。所以它应该 已经安装在你的电脑中了!检查在线文档以了解更多细节。 在 main() 函数中是如下代码: 这行代码完成这个简单程序的所有工作:在屏幕上打印文本。这里有四个重要的细节需要注意。首先 Rust 的缩进风格使用 4 个空格,而不是 1 个制表符(tab)。 第二, println! 调用了一个 Rust 宏(macro)。如果是调用函数,则应输入 println(没 有 !)。我们将在第十九章详细讨论宏。现在你只需记住,当看到符号 ! 的时候,就意味着调用的 是宏而不是普通函数,并且宏并不总是遵循与函数相同的规则。 第三, "Hello, world!" 是一个字符串。我们把这个字符串作为一个参数传递给 println!,字符 串将被打印到屏幕上。 第四,该行以分号结尾( ;),这代表一个表达式的结束和下一个表达式的开始。大部分 Rust 代码 行以分号结尾。 编译和运行是彼此独立的步骤 你刚刚运行了一个新创建的程序,那么让我们检查此过程中的每一个步骤。 在运行 Rust 程序之前,必须先使用 Rust 编译器编译它,即输入 rustc 命令并传入源文件名称,如 下: 如果你有 C 或 C++ 背景,就会发现这与 gcc 和 clang 类似。编译成功后,Rust 会输出一个二进制 的可执行文件。 在 Linux、macOS 或 Windows 的 PowerShell 上,在 shell 中输入 ls 命令可以看见这个可执行文 件。在 Linux 和 macOS,你会看到两个文件。在 Windows PowerShell 中,你会看到同使用 CMD 相同的三个文件。 println!("Hello, world!"); $ rustc main.rs $ ls main main.rs
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 12/525 在 Windows 的 CMD 上,则输入如下内容: 这展示了扩展名为 .rs 的源文件、可执行文件(在 Windows 下是 main.exe,其它平台是 main),以 及当使用 CMD 时会有一个包含调试信息、扩展名为 .pdb 的文件。从这里开始运行 main 或 main.exe 文件,如下: 如果 main.rs 是上文所述的 “Hello, world!” 程序,它将会在终端上打印 Hello, world!。 如果你更熟悉动态语言,如 Ruby、Python 或 JavaScript,则可能不习惯将编译和运行分为两个单独 的步骤。Rust 是一种 预编译静态类型(ahead-of-time compiled)语言,这意味着你可以编译程序, 并将可执行文件送给其他人,他们甚至不需要安装 Rust 就可以运行。如果你给他人一个 .rb、.py 或 .js 文件,他们需要先分别安装 Ruby,Python,JavaScript 实现(运行时环境,VM)。不过在这些 语言中,只需要一句命令就可以编译和运行程序。这一切都是语言设计上的权衡取舍。 仅仅使用 rustc 编译简单程序是没问题的,不过随着项目的增长,你可能需要管理你项目的方方面 面,并让代码易于分享。接下来,我们要介绍一个叫做 Cargo 的工具,它会帮助你编写真实世界中的 Rust 程序。 Hello, Cargo! ch01-03-hello-cargo.md commit c032e945406dd8ef2ce2062aa1f9d65dbf9020d6 Cargo 是 Rust 的构建系统和包管理器。大多数 Rustacean 们使用 Cargo 来管理他们的 Rust 项目, 因为它可以为你处理很多任务,比如构建代码、下载依赖库并编译这些库。(我们把代码所需要的库 叫做 依赖(dependencies))。 最简单的 Rust 程序,比如我们刚刚编写的,没有任何依赖。所以如果使用 Cargo 来构建 “Hello, world!” 项目,将只会用到 Cargo 构建代码的那部分功能。在编写更复杂的 Rust 程序时,你将添加 依赖项,如果使用 Cargo 启动项目,则添加依赖项将更容易。 由于绝大多数 Rust 项目使用 Cargo,本书接下来的部分假设你也使用 Cargo。如果使用 “安装” 部分 介绍的官方安装包的话,则自带了 Cargo。如果通过其他方式安装的话,可以在终端输入如下命令检 查是否安装了 Cargo: > dir /B %= the /B option says to only show the file names =% main.exe main.pdb main.rs $ ./main # Windows 是 .\main.exe
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 13/525 如果你看到了版本号,说明已安装!如果看到类似 command not found 的错误,你应该查看相应安 装文档以确定如何单独安装 Cargo。 使用 Cargo 创建项目 我们使用 Cargo 创建一个新项目,然后看看与上面的 “Hello, world!” 项目有什么不同。回到 projects 目录(或者你存放代码的目录)。接着,可在任何操作系统下运行以下命令: 第一行命令新建了名为 hello_cargo 的目录。我们将项目命名为 hello_cargo,同时 Cargo 在一个同名 目录中创建项目文件。 进入 hello_cargo 目录并列出文件。将会看到 Cargo 生成了两个文件和一个目录:一个 Cargo.toml 文 件,一个 src 目录,以及位于 src 目录中的 main.rs 文件。它也在 hello_cargo 目录初始化了一个 git 仓库,以及一个 .gitignore 文件。如果你在现有的 git 仓库中运行 cargo new,则不会生成 git 文 件;你可以通过使用 cargo new --vcs=git 来覆盖此行为。 注意:Git 是一个常用的版本控制系统(version control system, VCS)。可以通过 --vcs 参 数使 cargo new 切换到其它版本控制系统(VCS),或者不使用 VCS。运行 cargo new -- help 参看可用的选项。 请自行选用文本编辑器打开 Cargo.toml 文件。它应该看起来如示例 1-2 所示: 文件名: Cargo.toml 示例 1-2: cargo new 命令生成的 Cargo.toml 的内容 这个文件使用 TOML (Tom's Obvious, Minimal Language) 格式,这是 Cargo 配置文件的格式。 第一行, [package],是一个片段(section)标题,表明下面的语句用来配置一个包。随着我们在 这个文件增加更多的信息,还将增加其他片段(section)。 $ cargo --version $ cargo new hello_cargo $ cd hello_cargo [package] name = "hello_cargo" version = "0.1.0" edition = "2018" [dependencies]
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 14/525 接下来的三行设置了 Cargo 编译程序所需的配置:项目的名称、版本以及要使用的 Rust 版本。附录 E 会介绍 edition 的值。 最后一行, [dependencies],是罗列项目依赖的片段的开始。在 Rust 中,代码包被称为 crates。这 个项目并不需要其他的 crate,不过在第二章的第一个项目会用到依赖,那时会用得上这个片段。 现在打开 src/main.rs 看看: 文件名: src/main.rs Cargo 为你生成了一个 “Hello, world!” 程序,正如我们之前编写的示例 1-1!目前为止,之前项目与 Cargo 生成项目的区别是 Cargo 将代码放在 src 目录,同时项目根目录包含一个 Cargo.toml 配置文 件。 Cargo 期望源文件存放在 src 目录中。项目根目录只存放 README、license 信息、配置文件和其他 跟代码无关的文件。使用 Cargo 帮助你保持项目干净整洁,一切井井有条。 如果没有使用 Cargo 开始项目,比如我们创建的 Hello,world! 项目,可以将其转化为一个 Cargo 项 目。将代码放入 src 目录,并创建一个合适的 Cargo.toml 文件。 构建并运行 Cargo 项目 现在让我们看看通过 Cargo 构建和运行 “Hello, world!” 程序有什么不同!在 hello_cargo 目录下,输 入下面的命令来构建项目: 这个命令会创建一个可执行文件 target/debug/hello_cargo (在 Windows 上是 target\debug\hello_cargo.exe),而不是放在目前目录下。可以通过这个命令运行可执行文件: 如果一切顺利,终端上应该会打印出 Hello, world!。首次运行 cargo build 时,也会使 Cargo 在项目根目录创建一个新文件:Cargo.lock。这个文件记录项目依赖的实际版本。这个项目并没有依 赖,所以其内容比较少。你自己永远也不需要碰这个文件,让 Cargo 处理它就行了。 fn main() { println!("Hello, world!"); } $ cargo build Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs $ ./target/debug/hello_cargo # 或者在 Windows 下为 .\target\debug\hello_cargo.exe Hello, world!
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 15/525 我们刚刚使用 cargo build 构建了项目,并使用 ./target/debug/hello_cargo 运行了程序,也 可以使用 cargo run 在一个命令中同时编译并运行生成的可执行文件: 注意这一次并没有出现表明 Cargo 正在编译 hello_cargo 的输出。Cargo 发现文件并没有被改变, 就直接运行了二进制文件。如果修改了源文件的话,Cargo 会在运行之前重新构建项目,并会出现像 这样的输出: Cargo 还提供了一个叫 cargo check 的命令。该命令快速检查代码确保其可以编译,但并不产生可 执行文件: 为什么你会不需要可执行文件呢?通常 cargo check 要比 cargo build 快得多,因为它省略了生 成可执行文件的步骤。如果你在编写代码时持续的进行检查, cargo check 会加速开发!为此很多 Rustaceans 编写代码时定期运行 cargo check 确保它们可以编译。当准备好使用可执行文件时才 运行 cargo build。 我们回顾下已学习的 Cargo 内容: 可以使用 cargo build 构建项目。 可以使用 cargo run 一步构建并运行项目。 可以在不生成二进制文件的情况下构建项目并使用 cargo check 检查错误。 有别于将构建结果放在与源码相同的目录,Cargo 会将其放到 target/debug 目录。 使用 Cargo 的一个额外的优点是,不管你使用什么操作系统,其命令都是一样的。所以从现在开始本 书将不再为 Linux 和 macOS 以及 Windows 提供相应的命令。 发布(release)构建 当项目最终准备好发布时,可以使用 cargo build --release 来优化编译项目。这会在 target/release 而不是 target/debug 下生成可执行文件。这些优化可以让 Rust 代码运行的更快,不过 $ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/hello_cargo` Hello, world! $ cargo run Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs Running `target/debug/hello_cargo` Hello, world! $ cargo check Checking hello_cargo v0.1.0 (file:///projects/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 16/525 启用这些优化也需要消耗更长的编译时间。这也就是为什么会有两种不同的配置:一种是为了开发, 你需要经常快速重新构建;另一种是为用户构建最终程序,它们不会经常重新构建,并且希望程序运 行得越快越好。如果你在测试代码的运行时间,请确保运行 cargo build --release 并使用 target/release 下的可执行文件进行测试。 把 Cargo 当作习惯 对于简单项目, Cargo 并不比 rustc 提供了更多的优势,不过随着开发的深入,终将证明其价值。 对于拥有多个 crate 的复杂项目,交给 Cargo 来协调构建将简单的多。 即便 hello_cargo 项目十分简单,它现在也使用了很多在你之后的 Rust 生涯将会用到的实用工 具。其实,要在任何已存在的项目上工作时,可以使用如下命令通过 Git 检出代码,移动到该项目目 录并构建: 关于更多 Cargo 的信息,请查阅 其文档。 总结 你已经准备好开启 Rust 之旅了!在本章中,你学习了如何: 使用 rustup 安装最新稳定版的 Rust 更新到新版的 Rust 打开本地安装的文档 直接通过 rustc 编写并运行 Hello, world! 程序 使用 Cargo 创建并运行新项目 是时候通过构建更实质性的程序来熟悉读写 Rust 代码了。所以在第二章我们会构建一个猜猜看游戏 程序。如果你更愿意从学习 Rust 常用的编程概念开始,请阅读第三章,接着再回到第二章。 编写 猜猜看 游戏 ch02-00-guessing-game-tutorial.md commit 24ec8683571d6eaee21c5ce421abe39690647193 $ git clone example.org/someproject $ cd someproject $ cargo build
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 17/525 让我们一起动手完成一个项目,来快速上手 Rust!本章将介绍 Rust 中一些常用概念,并通过真实的 程序来展示如何运用它们。你将会学到 let、 match、方法、关联函数、外部 crate 等知识!在后 续章节,我们会深入探讨这些概念的细节。在这一章,我们将做基础练习。 我们会实现一个经典的新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会请玩家猜一个数并输入,然后提示猜测是大了还是小了。如果猜对 了,它会打印祝贺信息并退出。 准备一个新项目 要创建一个新项目,进入第一章中创建的 projects 目录,使用 Cargo 新建一个项目,如下: 第一个命令, cargo new,它获取项目的名称( guessing_game)作为第一个参数。第二个命令进 入到新创建的项目目录。 看看生成的 Cargo.toml 文件: 文件名: Cargo.toml 正如第一章那样, cargo new 生成了一个 “Hello, world!” 程序。查看 src/main.rs 文件: 文件名: src/main.rs 现在使用 cargo run 命令,一步完成 “Hello, world!” 程序的编译和运行: $ cargo new guessing_game $ cd guessing_game [package] name = "guessing_game" version = "0.1.0" edition = "2018" # See more keys and their definitions at https://doc.rust- lang.org/cargo/reference/manifest.html [dependencies] fn main() { println!("Hello, world!"); }
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 18/525 当你需要在项目中快速迭代时, run 命令就能派上用场,正如我们在这个游戏项目中做的,在下一次 迭代之前快速测试每一次迭代。 重新打开 src/main.rs 文件。我们将会在这个文件中编写全部的代码。 处理一次猜测 猜猜看程序的第一部分请求和处理用户输入,并检查输入是否符合预期的格式。首先,允许玩家输入 猜测。在 src/main.rs 中输入示例 2-1 中的代码。 文件名: src/main.rs 示例 2-1:获取用户猜测并打印的代码 这些代码包含很多信息,我们一行一行地过一遍。为了获取用户输入并打印结果作为输出,我们需要 将 io输入/输出库引入当前作用域。 io 库来自于标准库,也被称为 std: 默认情况下,Rust会将少量标准库中定义的模块引入到每个程序的作用域中。这些模块称作 prelude,你可以在标准库文档中了解到更多。 如果你需要的类型不在 prelude 中,你必须使用 use 语句显式地将其引入作用域。 std::io 库提供 很多有用的功能,包括接收用户输入的功能。 $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 1.50s Running `target/debug/guessing_game` Hello, world! use std::io; fn main() { println!("Guess the number!"); println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); println!("You guessed: {}", guess); } use std::io;
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 19/525 如第一章所提及, main 函数是程序的入口点: fn 语法声明了一个新函数, () 表明没有参数, { 作为函数体的开始。 第一章也提及了 println! 是一个在屏幕上打印字符串的宏: 这些代码仅仅打印提示,介绍游戏的内容然后请求用户输入。 使用变量储存值 接下来,创建一个储存用户输入的变量,像这样: 现在程序开始变得有意思了!这一小行代码发生了很多事。我们使用 let 声明来创建变量。我们在 变量名前添加 mut 以使其可变。 这行代码新建了一个叫做 apples 的变量并把它绑定到值 5 上。在 Rust 中,变量默认是不可变的。 我们将会在第三章的 “变量与可变性” 部分详细讨论这个概念。下面的例子展示了如何在变量名前使用 mut 来使一个变量可变: 注意: // 语法开始一个注释,持续到行尾。Rust 忽略注释中的所有内容。我们将会在第三章 中更详细地介绍注释。 回到猜猜看程序中。现在我们知道了 let mut guess 会引入一个叫做 guess 的可变变量。等号 ( =)告诉Rust我们现在想将某个值绑定在变量上。等号的右边是 guess 所绑定的值,它是 String::new 的结果,这个函数会返回一个 String 的新实例。 String 是一个标准库提供的字符 串类型,它是 UTF-8 编码的可增长文本块。 fn main() { println!("Guess the number!"); println!("Please input your guess."); let mut guess = String::new(); let apples = 5; let apples = 5; // immutable let mut bananas = 5; // mutable
12/18/21, 12:32 PM Rust 程序设计语言 简体中文版 https://kaisery.github.io/trpl-zh-cn/print.html 20/525 ::new 那一行的 :: 语法表明 new 是 String 类型的一个关联函数。关联函数(associated function)是实现一种特定类型的函数,在这个例子中类型是 String。 new 函数创建了一个新的空字符串,你会发现很多类型上有 new 函数,因为它是创建类型实例的惯 用函数名。 总的来说, let mut guess = String::new(); 这一行创建了一个可变变量,当前它绑定到一个新 的 String 空实例上。 接收用户输入 回忆一下,我们在程序的第一行使用 use std::io; 从标准库中引入了输入/输出功能。现在调用可 以使我们处理用户输入的 io 库中的函数 stdin: 如果程序的开头没有使用 use std::io 引入 io 库,我们仍可以通过把函数调用写成 std::io::stdin 来使用函数。 stdin 函数返回一个 std::io::Stdin 的实例,这代表终端标准输 入句柄的类型。 接下来, .read_line(&mut guess) 这一行调用了 read_line 方法从标准输入句柄获取用户输入。 我们还将 &mut guess 作为参数传递给 read_line() ,以告诉它在哪个字符串存储用户输入 。 read_line 全部的工作是接收用户在标准输入中输入的任何内容,并将其追加到一个字符串中(不 覆盖其内容),因此我们传入字符串作为参数。这个字符串参数需要是可变的,以便该方法可以改变 字符串的内容。 & 表示这个参数是一个 引用(reference),它允许多处代码访问同一处数据,而无需在内存中多次 拷贝。引用是一个复杂的特性,Rust 的一个主要优势就是安全而简单的操纵引用。完成当前程序并不 需要了解如此多细节。现在,我们只需知道它像变量一样,默认是不可变的。因此,需要写成 &mut guess 来使其可变,而不是 &guess。(第四章会更全面的解释引用。) 使用 Result 类型来处理潜在的错误 我们还没有完全分析完这行代码。虽然我们已经讲到了文本中的第三行,但它仍是逻辑行(虽然换行 了但仍是语句)的一部分。后一部分是这个方法: 我们完全可以这样写: io::stdin().read_line(&mut guess) .expect("Failed to read line"); .expect("Failed to read line");