蚊章汇 - Pratham Patel
type: channel
link: https://linuxcn.bwsl.wang/author/1602/
description: Pratham Patel
items: Linux 内核动手编译实用指南
items: arm vs AArch64 vs amd64 vs x86_64 vs x86：有什么区别？
items: 什么是 CloudReady？它是 Chrome OS 的开源替代品吗？
items: 观点：红帽公司的自杀企图
items: Rust 基础系列 #8：编写里程碑 Rust 程序
items: Rust 基础系列 #7
items: Rust 基础系列 #6
items: Rust 基础系列 #5
items: Rust 基础系列 #4
items: Rust 基础系列 #3

Linux 内核动手编译实用指南
type: item
date: 2023-10-04T19:53:00+08:00
description: 一份让你深入体验最新 Linux 内核编译过程的实操指南。出于各种原因，自行编译 Linux 内核可能引起你的兴趣。这些原因可能包括但不限于：测试一个比你目前的 Linux 发行版更新的内核版本采用一组不同的配置选项、驱动来构建内核学习者的好奇心 ?此指南将一步步指导你如何亲自编译 Linux 内核，包括你该运行哪些命令，为什么运行这些命令以及这些命令的执行效果。本文篇幅较长，所以请做好准备！? 诸如 Ubuntu 这样的发行版提供了更简单地安装主线 Linux 内核的方式。但本教程目标是从源码手动完成所有工作。此教程需要你付出时间、耐心以及丰富的 Linux 命令行使用经验。本文更注重亲身实践的体验。不管怎么说，我仍建议你在虚拟机或备用系统中尝试此冒险，而非在你的主系统上进行。前置准备在软件领域，构建任何事物都有两个基本要求：源代码构建依赖因此，作为预备环节，我们需要下载 Linux 内核的源码压缩包，并安装一些能让我们成功构建 Linux 内核的依赖项。Linux 版本导览在任何时刻，Freax Linux 内核都有四种“版本”。Linux 的这些 “版本”，按照开发流程的顺序是：linux-next 树： 所有准备合并到 Linux 代码库的代码首先被合并到 linux-next 树。它代表的是 Linux 内核最新也是“最不稳定”的状态。大多数 Linux 内核开发者和测试人员使用这个来提高代码质量，为 Linus Torvalds 的后续提取做准备。请谨慎使用！发布候选版（RC） / 主线版： Linus 从 linux-next 树抽取代码并创建一个初始发布版本。这个初始发布版本的测试版称为 RC（ 发布候选 Release Candidate ）版本。一旦 RC 版本发布，Linus 只会接受对它的错误修复和性能退化相关的补丁。基础这些反馈，Linus 会每周发布一个 RC 内核，直到他对代码感到满意。RC 发行版本的标识是 -rc 后缀，后面跟一个数字。稳定版： 当 Linus 觉得最新的 RC 版本已稳定时，他会发布最终的“公开”版本。稳定发布版将会维护几周时间。像 Arch Linux 和 Fedora Linux 这样的前沿 Linux 发行版会使用此类版本。我建议你在试用 linux-next 或任何 RC 版本之前，先试一试此版本。LTS 版本： 每年最后一个稳定版将会再维护 几年。这通常是一个较旧的版本，但它会 会积极地维护并提供安全修复。Debian 的稳定版本会使用 Linux 内核的 LTS 版版本。若想了解更多此方面的知识，可参阅 官方文档。本文将以当前可用的最新稳定版为例，编写此文时的 Linux 内核版本是 6.5.5。系统准备由于 Linux 内核使用 C 语言编写，编译 Linux 内核至少需要一个 C 编译器。你的计算机上可能还需要其他一些依赖项，现在是安装它们的时候了。? 这个指南主要聚焦于使用 GNU C 编译器（GCC）来编译 Linux 内核。但在未来的文章中（可能会深入介绍 Rust 的支持），我可能会介绍使用 LLVM 的 Clang 编译器作为 GCC 的替代品。不过，请注意，MSVC 并不适用。尽管如此，我仍期待有微软的员工为此发送修补程序集。我在瞎想啥？对于 Arch Linux 以及其衍生版本的用户，安装命令如下：sudo pacman -S base-devel bc coreutils cpio gettext initramfs kmod libelf ncurses pahole perl python rsync tar xz 对于 Debian 以及其衍生版本的用户，安装命令如下：sudo apt install bc binutils bison dwarves flex gcc git gnupg2 gzip libelf-dev libncurses5-dev libssl-dev make openssl pahole perl-base rsync tar xz-utils 对于 Fedora 以及其衍生版本的用户，安装命令如下：sudo dnf install binutils ncurses-devel \ /usr/include/{libelf.h,openssl/pkcs7.h} \ /usr/bin/{bc,bison,flex,gcc,git,gpg2,gzip,make,openssl,pahole,perl,rsync,tar,xz,zstd} 下载 Linux 内核源码请访问 kernel.org，在页面中寻找第一个 稳定 Stable 版本。你不会找不到它，因为它是最显眼的黄色方框哦 ?点击访问 kernel.org通…

arm vs AArch64 vs amd64 vs x86_64 vs x86：有什么区别？
type: item
date: 2023-09-25T17:28:00+08:00
description: 当涉及到 CPU 的时候，有许多术语：AArch64、x86\_64、amd64、arm 等等。了解它们是什么以及它们之间的区别。当你查看数据表或软件下载页面时是否被 ARM、AArch64、x86_64、i386 等术语混淆？这些被称为 CPU 架构，我会帮你深入了解这个计算话题。以下的表将为你总结每个字符串所代表的意义：CPU 架构描述x86_64 /x86/amd6464 位 AMD/英特尔 CPU 的别称AArch64 /arm64/ARMv8/ARMv964 位 ARM CPU 的别称i38632 位 AMD/英特尔 CPUAArch32 /arm/ARMv1 到 ARMv732 位 ARM CPU 的别称rv64gc /rv64g64 位 RISC-V CPU 的别称ppc64le64 位 PowerPC CPU，小端字节序存储从左到右是使用该术语来描述 CPU 架构超过其右侧其他可选用术语的偏好。从左到右是使用该术语描述 CPU 架构的优先级，使用左侧的而不是其右侧的其他可供选择的术语。如果你像我一样是个极客，并想要更深入地解释，请继续阅读！概述：CPU 架构通常来说，我之前列出的术语是描述 CPU 架构的。但严格讲，它们被计算机工程师视为 CPU 的 指令集架构 Instruction Set Architecture （ISA）。CPU 的指令集架构定义了 CPU 如何解析二进制代码中的 1 和 0。这些 CPU 的 ISA 有几个主要的类别：x86（AMD/英特尔）ARMRISC-VPowerPC（IBM 仍在使用）当然，还有更多种类的 CPU ISA，比如 MIPS、SPARC、DEC Alpha 等等。但我列出的这些至今仍然被广泛使用（以某种形式）。上述列出的 ISA 主要根据 内存总线的宽度 分为至少两个子集。内存总线的宽度指的是 CPU 和 RAM 一次能传输的位数。内存总线有很多种宽度，但最常见的是 32 位和 64 位。? 32 位的 CPU ISA 要么是已经过时的历史产物，被留下来要么只是为了支持旧的系统，要么只运用在微控制器中。可以说，所有新的硬件都已经是 64 位的了，特别是那些面向消费者的硬件。x86（AMD/英特尔）x86 CPU 的指令集架构主要源于英特尔，因为英特尔是最初搭配 8085 微处理器创建了它。8085 微处理器的内存总线宽度为 16 位。而后来，AMD 加入了这个领域，并且一直紧随英特尔的步伐，直到 AMD 创建出了自己的超集 64 位架构，超过了英特尔。x86 架构的子集如下：i386：如果你拥有的是 2007 年之前的 CPU，那么这可能就是你的 CPU 架构。它是现在使用的 AMD/英特尔的 x86 架构的 32 位“版本”。x86_64/x86/amd64：这三个术语在不同的项目中可能会被交替使用。 但它们都是指 x86 AMD/英特尔架构的 64 位“版本”。无论如何，x86_64 这个字符串比 x86 和 amd64 使用得更广泛（也更受欢迎）。例如，FreeBSD 项目称 64 位的 x86 架构为 amd64，而 Linux 和 macOS 则称之为 x86_64。? 由于 AMD 在创造 64 位 ISA 上超越了英特尔，所以一些项目（比如 FreeBSD）把 x86 的 64 位版本称为 amd64。但更被广泛接受的术语还是 x86\_64。对于 CPU ISA，“x86” 这个字符串是一种特殊的情况。你要知道，在从 32 位的 x86（i386）到 64 位的 x86（x86_64）的过渡过程中，CPU 制造商确保了 CPU 能够运行 32 位 和 64 位指令。所以，有时你可能会看到 x86 也被用来意指“这款产品只能运行在 64 位的计算机上，但如果该计算机能运行 32 位指令，那么你也可以在它上面运行 32 位的用户软件”。这种 x86 的模糊性——也就是诸如能同时运行 32 位代码的 64 位处理器——其主要用于和存在于运行在 64 位处理器上的，但是允许用户运行 32 位软件的操作系统，Windows 就通过这种被称作“兼容模式”的特性运用了这种方式。汇总一下，由 AMD 和 英特尔 设计的 CPU 有两种架构：32 位的（i386）和 64 位的（x86_84）。其它的英特尔x86_64 ISA 实际上有几个子集。这些子集都是 64 位，但它们新添加了诸如 SIMD（ 单指令多数据 Single Instruction Multiple Data ）指令等功能。x86_64-v1：这是大多数人都熟知的基础 x86_64 ISA。当人们谈论 x86_64 时，他们通常指的就是 x86_64-v1 ISA。x86_64-v2：此版本新增了…

什么是 CloudReady？它是 Chrome OS 的开源替代品吗？
type: item
date: 2023-07-11T22:32:00+08:00
description: CloudReady 日趋流行，尤其是在使用低端硬件的用户中。我决定了解一下它并分享我的发现。什么是 CloudReady？CloudReady 是一个基于谷歌的 Chromium OS 开源代码仓库的操作系统。Neverware 公司是 CloudReady 背后的组织，它开发的 CloudReady 可以部署到现有的 PC 和 Mac 上，由于其对硬件的要求极低，因此可以保证在上述硬件上的性能提升。大体上来说，CloudReady 把你的旧计算机变成了 Chromebook。2020 年底 Google 收购了 Neverware。在分享我的经验和想法之前，让我先进一步介绍以下它。谁应该尝试 CloudReady？CloudReady 主要是面向那些能够从使用类 Chromebook 设备中获益，但是已经在硬件设备上进行了投入的机构。下面是我想到的一些例子：经过 CloudReady 扩展的 Chromium OS 用户界面足够简单，用户从 macOS 或 Windows 转向使用 CloudReady 几乎不需要培训。更好的安全性，用户不会被安装充斥于 macOS 和 Windows 上的恶意软件。Chromium OS 的硬件要求低，能够在老旧的硬件上良好运行。通过“ 谷歌管理控制台 Google Admin Console ”来管理计算机。简单初始安装过程。下面是运行 CloudReady 的最低硬件要求：CPU：任何 2008 年之后的 CPU （没有提及对 ARM 架构 CPU 的支持情况，暂且认为仅支持 x86 架构 CPU，即英特尔和 AMD 的 CPU）内存：2 GB 及以上存储：16GB 及以上具备访问 BIOS 或 UEFI 的权限——为了能够从 USB 安装程序启动如果你不确定自己的上网本是否能够运行 CloudReady，Neverware 公司发布了能够运行 CloudReady 的认证机型清单。目前该清单已包含超过 450 种机型。你可以通过 这个官方链接来核查自己的机型。CloudReady 与 Chrome OS 有什么不同？如果你的主要使用目的是以下场景，那么 CloudReady 能够满足你的需要：通过 “ Neverware 管理门户 Neverware Admin Portal ”（到 Google 完成收购为止）或 “ 谷歌管理控制台 Google Admin Console ” 来管理 CloudReady 设备。你的工作可以通过网页浏览器来完成（基于网络服务）。当你了解到“CloudReady 是一个基于 Chrome OS 的操作系统”时，你一定认为它至少应该能够运行安卓应用。遗憾的是，事实并非如此。开源的 Chromium OS 不支持安卓运行时框架/服务，因此在 CloudReady 中不可用。由于一些法律和技术上的原因，Neverware 并没有将安卓运行时添加到 CloudReady 中。另一方面它也阻止你 侧载 side-loading APK，因为根本没有运行安卓应用的东西。当我试图从 应用抽屉 app drawer 启动 Play Store 时，它在浏览器中打开了谷歌 Play Store 的网页。这真是个坏消息。然而得益于 CloudReady 是基于“ 面向网络 web focused”的操作系统的，我的 Chromium 浏览器扩展运行良好。所以如果你想通过 CloudReady 把自己的旧笔记本变成一个非触屏的平板，那你就不太走运了。为什么会有 CloudReady？你也许会疑惑，既然已经有了 Chrome OS，为什么 Neverware 还要投入资源开发 CloudReady 这个 “克隆体” 呢？仔细观察运行 Chrome OS 的设备，你就会发现它们都是预装设备。也就是说 Chrome OS 只适用于生产 Chromebook 的 OEM 厂商。对于微软的 Windows，OEM 厂商可以预装 Windows，用户也可以下载单独的 ISO。然而，谷歌并不提供可用于在电脑上安装 Chrome OS 的 ISO。所以需要开发一个基于 Chromium OS 的操作系统，让你能够将其安装到已有的 PC 或 Mac 上。CloudReady 为你提供一种安装基于 Chromium OS，企业用户也可以通过 Neverware 获得官方支持服务。获取 CloudReadyCloudReady 提供三个版本：家庭版（免费）、教育版（付费）和企业版（付费）。如果你想先体验一下 CloudReady，那就选家庭版。Neverware 不提供 ISO 镜像，但它提供一个启动 U 盘制作工具。这个工具仅限 Windows 操作系统。Neverware 同时提供一个…

观点：红帽公司的自杀企图
type: item
date: 2023-07-03T01:20:00+08:00
description: 红帽公司将其源代码放在付费墙后面的最新决定，今天可能会对其直接竞争对手造成一定伤害，但对红帽公司自身而言，这将对之后产生负面影响。基于我对 RHEL 和其他红帽产品的热爱，我不得不现在表达一些严厉的看法。我之所以这样做，是因为我希望 RHEL [及/或其克隆版] 对每个人都是 可获得的。请原谅这次我用词有些激烈，因为我从内心不希望 RHEL 成为 对业余爱好者不可及 的产品。免费的 RHEL 开发者许可证只是转移话题而已，相比于 真正的 RHEL 克隆版存在的时候，它削弱了 广大动手爱好者社区 的热情。?️ 对于我的使用场景来说，使用免费订阅的 RHEL 使我感到满意。实际上，我正在使用的就是免费订阅。本文是关于如果保持这种状态会对 RHEL 产生怎样的影响。快速回顾：事情是如何走到这一步的我相信你现在都已经知道为什么红帽公司最近频频出现在新闻中：但是，如果你对正在发生的事情一无所知，让我给你一个快速时间线的指引，帮助你理解：红帽公司拥有一个出色的 Linux 发行版，名为 红帽企业 Linux Red Hat Enterprise Linux （RHEL），支持周期长达 10 年。与其他任何 Linux 发行版一样，用于构建 RHEL（或 RHEL 的 克隆版）的源代码是公开可用的。CentOS 利用上述源代码，去除商标，并创建了一个几乎 1 比 1 的 RHEL 拷贝。（我说“几乎”是因为 RHEL 的商标 必须 被删除。）那些不愿意支付费用只是为了第一次尝试 RHEL 的爱好者社区，现在可以使用 CentOS 来看看是否喜欢它。这个人中的一部分人随后向他们的高层管理人员介绍了 RHEL 及其 通过 CentOS 初步体验 到的卓越表现，他们很可能转向在 RHEL 上支持他们的产品或在部署中使用 RHEL。由于 CentOS 是免费的，爱好者社区得以扩大。像 Jeff Geerling 这样的人使用 CentOS 来教授对 扮演系统管理员感兴趣 的新一代爱好者，让他们了解他的 开源存储库 中诸如 Ansible 之类的很棒的内容。CentOS 的二进制文件在 RHEL 更新后几周才发布。因此，红帽收购了 CentOS，以确保 CentOS 不会落后于 RHEL 的更新。几年后，CentOS Stream 的惨淡出场，导致了 CentOS 的消亡。Rocky Linux 和 Alma Linux 出现填补了 CentOS 留下的空白。它们使用的是公开可用的 RHEL 源代码。红帽公司开始限制源代码的访问，只允许其付费客户以及任何拥有免费的开发者订阅的人访问。? 我特意没有提到 IBM 收购红帽的事情，因为如果红帽的某个人有勇气在 官方场合 公开说，“只是简单地重建代码，没有增加价值或以任何方式改变代码，对开源公司来说都是真正的威胁。”，他们肯定也可以直承 IBM 参与了这些决定。但几乎每个红帽员工都公开否认了这种指责。所以我也不相信 IBM 自己搬起石头砸自己的脚。但你可以根据自己的心来决定是否相信。不，当我说 IBM 可能没有参与这个决定时，我并不是在讽刺。他们 有可能参与，但我不这么认为。亲爱的红帽公司，你刚刚做了什么？你向社区提供一款免费的产品（CentOS）。然后你在提供这个免费产品的过程中（CentOS）改变它的的支持周期，并将其“替代品”（CentOS Stream）用作你闪亮的企业产品（RHEL）的“测试场”。然后那些水蛭们拿起你闪亮产品的源代码，创建了一个 CentOS 的接替者（Rocky Linux 和 Alma Linux）。你不喜欢这样，所以对它们进行了 “软付费墙” 的限制。现在，虽然 你有权利这样做（因为你从为 RHEL 提供支持而不是 RHEL 本身赚钱），但我会解释为什么这对 你自己来说 是一个糟糕的举动。问题： RHEL 在企业环境中为什么如此受欢迎？答案： CentOS Stream。问题： 在线教程用什么来教授 RHEL？答案： CentOS Stream。问题： 那些跟随上述在线教程的学习者用什么来学习 RHEL？答案： CentOS Stream。问题： 当这些学生成为老师时，他们会向询问如何入门 Linux 系统管理员的人推荐什么？答案： CentOS Stream。问题： 如果有人愿意购买 RHEL 的许可证，但因为没有公开的软件仓库而犹豫不决，他们会用什么？答案： CentOS Stream。简而言之，如果红帽公司继续对 RHEL 的克隆产品制造麻烦，以下是可能出现的情况：许多参与企业部署的客户和专业人士将考虑放弃 RHEL，并且不再对其提供支持。新用户将开始考虑使用 Ubuntu、Debian、openSUSE 或其他长期存在的替代产品。大学和 IT 培训也将转向替代方案，例…

Rust 基础系列 #8：编写里程碑 Rust 程序
type: item
date: 2023-06-23T19:44:00+08:00
description: 到目前为止，我们已经讲解了包括 变量、可变性、常量、数据类型、函数、if-else 语句 和 循环 在内的一些关于 Rust 编程的基础知识。在 Rust 基础系列的最后一章里，让我们现在用 Rust 编写一个程序，使用这些主题，以便更好地理解它们在现实世界中的用途。让我们来编写一个相对简单的程序，用来从水果市场订购水果。我们程序的基本结构来让我们首先向用户问好，并告诉他们如何与程序交互。fn main() { println!("欢迎来到水果市场！"); println!("请选择要购买的水果。\n"); println!("\n可以购买的水果：苹果、香蕉、橘子、芒果、葡萄"); println!("购买完成后，请输入“quit”或“q”。\n"); } 获取用户输入上面的代码非常简单。目前，你不知道接下来该做什么，因为你不知道用户接下来想做什么。所以让我们添加一些代码，接受用户输入并将其存储在某个地方以便稍后解析，然后根据用户输入采取适当的操作。use std::io; fn main() { println!("欢迎来到水果市场！"); println!("请选择要购买的水果。\n"); println!("\n可以购买的水果：苹果、香蕉、橘子、芒果、葡萄"); println!("购买完成后，请输入“quit”或“q”。\n"); // 获取用户输入 let mut user_input = String::new(); io::stdin() .read_line(&mut user_input) .expect("无法读取用户输入。"); } 有三个新元素需要告诉你。所以让我们对这些新元素进行浅层次的探索。1. 理解 use 关键字在这个程序的第一行，你可能已经注意到我们“使用”（哈哈！）了一个叫做 use 的新关键字。Rust 中的 use 关键字类似于 C/C++ 中的 #include 指令和 Python 中的 import 关键字。使用 use 关键字，我们从 Rust 标准库 std 中“导入”了 io（输入输出）模块。LCTT 译注：“使用”在原文中为“use”，与新介绍的关键字一样。你可能会想知道为什么我们在可以使用 println 宏来将某些内容输出到标准输出时，导入 io 模块是必要的。Rust 的标准库有一个叫做 prelude 的模块，它会自动被包含。该模块包含了 Rust 程序员可能需要使用的所有常用函数，比如 println 宏。（你可以在 这里 阅读更多关于 std::prelude 模块的内容。）Rust 标准库 std 中的 io 模块是接受用户输入所必需的。因此，我们在程序的第一行添加了一个 use 语句。2. 理解 Rust 中的 String 类型在第 11 行，我创建了一个新的可变变量 user_input，正如它的名字所表示的那样，它将被用来存储用户输入。但是在同一行，你可能已经注意到了一些“新的”东西（哈哈，又来了！）。LCTT 译注：“新的”在原文中为“new”，在第 11 行的代码中，原作者使用了 String::new() 函数，所以此处的梗与“使用”一样，原作者使用了一个在代码中用到的单词。我没有使用双引号（""）声明一个空字符串，而是使用 String::new() 函数来创建一个新的空字符串。"" 与 String::new() 的区别是你将在 Rust 系列的后续文章中学习到的。现在，只需要知道，使用 String::new() 函数，你可以创建一个可变的，位于堆上的字符串。如果我使用 "" 创建了一个字符串，我将得到一个叫做“字符串切片”的东西。字符串切片的内容也位于堆上，但是字符串本身是不可变的。所以，即使变量本身是可变的，作为字符串存储的实际数据是不可变的，需要被覆盖而不是修改。3. 接受用户输入在第 12 行，我调用了 std::io 的 stdin() 函数。如果我在程序的开头没有导入 std::io 模块，那么这一行将是 std::io::stdin() 而不是 io::stdin()。sdtin() 函数返回一个终端的输入句柄。read_line() 函数抓住这个输入句柄，然后，正如它的名字所暗示的那样，读取一行输入。这个函数接受一个可变字符串的引用。所以，我传入了 user_input 变量，通过在它前面加上 &mut，使它成为一个可变引用。⚠️ read_line() 函数有一个 怪癖。这个函数在用户按下回车键之后 停止 读取输入。因此，这个函数也会记录换行符（\n），并将一个换行符存储在你传入的可变字符串变量的结尾处。所以，请在处理它时要么考虑到这个换行符，要么将它删除。Rust 中的错误处理入门最后，在这个链的末尾有一个 expect…

Rust 基础系列 #7
type: item
date: 2023-06-16T00:42:19+08:00
description: 在 Rust 系列的 上一篇文章 中，我介绍了如何使用 if 和 else 关键字来处理 Rust 程序的控制流。这是处理程序控制流的一种方法。另一种方法是使用循环。因此，让我们在本文中看看循环。Rust 中可用的循环Rust 编程语言有三种不同的循环，基于你想要实现什么以及可用的内容：forwhileloop我假设你对 for 和 while 已经很熟悉了，但 loop 对你来说可能是个新概念。让我们先从熟悉的概念开始。for 循环for 循环主要用于迭代一种称为迭代器的东西。这个迭代器可以从任何东西中创建，从数组、向量（很快就会介绍！）、一系列值，或者任何自定义的东西。这里的可能性是无限的。来看看 for 循环的语法。for 迭代变量 in 迭代器 { <语句>; } 其中的 迭代变量 在大多数其他编程语言教程中通常被称为 i ; )迭代器 可以是任何东西，只要它能告诉下一个值是什么，如果有的话。来通过一个程序来理解这个。fn main() { let my_arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; println!("迭代数组"); for element in my_arr { println!("{}", element); } println!("\n迭代一个真正的迭代器"); for element in my_arr.iter() { println!("{}", element); } println!("\nPython 风格的范围"); for element in 0..10 { println!("{}", element); } } 这里，我声明了一个数组，它包含从 0 到 9 的 10 个数字。在第 5 行的 for 循环中，我只是将这个数组指定为迭代器，Rust 会自动处理对这个数组的所有元素的迭代。不需要花哨的 my_arr[i] 魔法。但是，在第 10 行，我调用了 .iter() 函数。这是一个明确的提及，它基于 my_arr 的值来获取一个迭代器。这个循环和第 5 行的循环之间唯一的区别是，这里你是通过在数组上调用 .iter() 函数来明确地调用它的。在这个上下文环境中，在一个数据类型上调用 .iter() 函数不是必须的。因为这是一个数组，是语言本身提供的一种数据类型，Rust 已经知道如何处理它了。但是你 需要 在自定义数据类型中使用它。最后，在第 15 行，我们有一个循环，它循环遍历一个范围。嗯，差不多是这样。如果你仔细看，这个范围看起来很像切片 “类型”。Rust 也知道这一点，并且 为 你处理了迭代（哈哈，明白了吗？）。LCTT 译注：此处的梗是，“为你处理了迭代” 的英文原文是 “handles iteration for you"，其中的 “for” 与 “for 循环” 的 “for” 是同一个单词。输出如下：迭代数组 0 1 2 3 4 5 6 7 8 9 迭代一个真正的迭代器 0 1 2 3 4 5 6 7 8 9 Python 风格的范围 0 1 2 3 4 5 6 7 8 9 while 循环while 循环可以被认为是非常类似于 if 条件语句。使用 if 语句，只要用户提供的条件为 true，if 语句体中的代码就会被执行 一次。但是，在 while 循环中，如果条件评估为 true，循环就会开始循环循环体。只要条件继续评估为 true，循环就会继续迭代。while 循环只有在循环完成当前迭代中所有语句的执行并且在检查条件时，它的结果为 false 时才会停止。来看看 while 循环的语法...while 条件 { <语句>; } 看到了吗？和 if 条件语句非常相似！不过没有 else 块 ; )来看一个程序来更好地理解这个。fn main() { let mut var = 0; while var < 3 { println!("{var}"); var += 1; } } 我有一个可变变量 var，它的初始值为 0。只要可变变量 var 中存储的值小于 3，while 循环就会执行。在循环中，var 的值被打印出来，然后它的值被增加 1。这是上面代码的输出：0 1 2 loop 循环Rust 有一个无限循环。是的，一个没有开始条件和停止条件的循环。它只是一直循环，直到永远。当然，它有触发器来停止代码本身的循环执行。无限循环的语法如下：loop { <语句>; } ? 这些循环主要用于 GUI 软件，退出是一个 显式 操作。在我给你一个例子之前，因为这个循环非常特殊，让我们先看看如何 退出 它 :p要停止无限循环的执行，需要在循环内使用 break 关键字。来看一个例子，只有 0 到 3 之间的整数（包括 0 和 3…

Rust 基础系列 #6
type: item
date: 2023-06-11T17:49:25+08:00
description: 在 上一篇文章 中，你学习了函数。在这篇文章中，我们将学习使用条件语句来管理 Rust 程序的控制流。什么是条件语句？在编写代码的时候，最常见的任务之一就是检查某些条件是否为 true 或 false。“如果温度高于 35°C，打开空调。”通过使用 if 和 else 这样的关键字（有时候结合使用），程序员可以根据条件（例如提供的参数数量、从命令行传递的选项、文件名、错误发生等）改变程序的行为。所以，对一个程序员来说，了解任何语言的控制流都是至关重要的，更不用说 Rust 了。条件运算符下表列出了所有常用的单个条件运算符：运算符示例解释>a > ba 大于 b<a < ba 小于 b==a == ba 等于 b!=a != ba 不等于 b>=a >= ba 大于 或 等于 b<=a <= ba 小于 或 等于 b以及下表是逻辑运算符，它们用于一个或多个条件之间：运算符示例解释` ` （逻辑或）`条件1 条件2`条件1 或 条件2 中至少有一个为 true&& （逻辑与）条件1 && 条件2所有 条件都为 true! （逻辑非）!条件条件 的布尔值的相反值? 与数学相似，你可以使用圆括号来指定操作的优先级。使用 if else要控制 Rust 代码的基本流程，使用两个关键字：if 和 else。这可以根据提供的条件的状态创建两个“执行路径”。一个简单的带有替代执行路径的 if 块的语法如下：if 条件 { <语句>; } else { <语句>; } ? 当只有一个条件时，将其括在圆括号中并不是强制性的。根据语法，使用圆括号是可选的。你仍然应该使用它们来指定优先级并优化可读性。来看看一个例子。fn main() { let a = 36; let b = 25; if a > b { println!("a 大于 b"); } else { println!("b 大于 a"); } } 这里，我声明了两个整数变量 a 和 b，它们的值分别为 '36' 和 '25'。在第 5 行，我检查变量 a 中存储的值是否大于变量 b 中存储的值。如果条件计算结果为 true，则会执行第 6 行的代码。如果条件计算结果为 false，由于我们有一个 else 块（可选），第 8 行的代码将被执行。来看看程序的输出。a 大于 b 完美！来修改一下变量 a 的值，使其小于变量 b 的值，看看会发生什么。我将把 a 的值改为 '10'。修改后的输出如下：b 大于 a 但是，如果我将相同的值存储在变量 a 和 b 中呢？为了看到这一点，我将两个变量的值都设置为 '40'。修改后的输出如下：b 大于 a 嗯？从逻辑上讲，这没有任何意义... :frowning:但是这可以改进！我们继续。使用 else if 条件与其他任何编程语言一样，你可以使用 else if 块来提供多于两个的执行路径。语法如下：if 条件 { <语句>; } else if 条件 { <语句>; } else { <语句>; } 现在，通过使用 else if 块，我可以改进程序的逻辑。下面是修改后的程序。fn main() { let a = 40; let b = 40; if a == b { println!("a 与 b 是相等的"); } else if a > b { println!("a 大于 b"); } else { println!("b 大于 a"); } } 现在，我的程序的逻辑是正确的。它已经处理了所有的边缘情况（我能想到的）。第 5 行处理了 a 等于 b 的情况。第 7 行处理了 a 可能大于 b 的情况。而 a 小于 b 的情况则由第 9 行的 else 块隐式处理。现在，当我运行这段代码时，我得到了以下输出：a 与 b 是相等的 现在这就完美了！示例：找到最大值我知道使用 if 和 else 很容易，但是让我们再看一个程序。这次，我们来比较三个数字。我还将在这个实例中使用逻辑运算符！fn main() { let a = 73; let b = 56; let c = 15; if (a != b) && (a != c) && (b != c) { if (a > b) && (a > c) { println!("a 是最大的"); } else if (b > a) && (b > c) { println!("b 是最大的"); } else { println!("c 是最大的"); } } } 这个程序第一眼看上去可能很复杂，但是不要害怕，我会解释的！最开始，我声明了三个变量 a、b 和 c，并赋予了我能想到的随机值。然后，在第 6 行，我检查了没有变量的值与其他变量相同的条件。首先，我检查 a 和 b 的值，然后是 a 和 …

Rust 基础系列 #5
type: item
date: 2023-05-29T23:06:30+08:00
description: 在这一章中，在实例的帮助下，学习如何使用函数并从中返回值。就跟任何现代编程语言一样，Rust 也有函数。你已经熟悉的函数是 main 函数。这个函数在程序启动时被调用。但是其他函数呢？在本文中，你将学习如何在 Rust 程序中使用函数。函数的基本语法你可能已经在我们声明 main 函数时知道了这一点，不管怎么样，还是让我们看一下声明函数的语法。// 声明函数 fn function_name() { <statement(s)>; } // 调用函数 function_name(); 来让我们看一个简单的函数，它将字符串 "Hi there!" 打印到标准输出。fn main() { greet(); } fn greet() { println!("Hi there!"); } ? 与 C 不一样的是，不管你是否要在声明或定义之前调用函数都没有关系。只要这个函数在 某个地方 被声明了，Rust 就会处理它。正如预期，它的输出如下：Hi there! 这挺简单的。让我们把它提升到下一个级别。让我们创建一个接受参数并返回值的函数。有没有参数和有没有返回值这两者之间无关。使用函数接受参数声明一个接受参数的函数的语法如下：// 声明函数 fn function_name(variable_name: type) { <statement(s)>; } // 调用函数 function_name(value); 你可以把函数参数想象成一个传递给函数的 元组。它可以接受多种数据类型的参数，而且你可以接受任意多个参数。所以，你不必局限于接受相同类型的参数。与某些语言不同的是，Rust 没有 默认参数。在调用函数时填充所有参数是强制性的。示例：饥饿函数来让我们看一个程序来更好地理解这个。fn main() { food(2, 4); } fn food(theplas: i32, rotis: i32) { println!( "我饿了... 我需要 {} 个葫芦巴叶饼和 {} 个罗提！", theplas, rotis ); } 在第 5 行，我声明了一个名为 food 的函数。这个函数接受 2 个参数：theplas 和 rotis（印度食物的名字）。然后我打印了这些变量的内容。对于 main 函数，我使用参数 2 和 4 调用 food 函数。这意味着 theplas 被赋值为 2，rotis 被赋值为 4。来让我们看一下程序的输出：我饿了... 我需要 2 个葫芦巴叶饼和 4 个罗提！ 我现在真的饿了... ?从函数返回值就像函数可以接受参数一样，函数也可以返回一个或多个值。这样的函数的语法如下：// 声明函数 fn function_name() -> data_type { <statement(s)>; } // 调用函数 let x = function_name(); 函数可以使用 return 关键字或者使用表达式而不是语句来返回一个值。等等！什么是表达式？在进一步之前：语句与表达式在讲解 Rust 函数的例子中提起这个可能不太合适，但是你应该理解 Rust 和其他编程语言中语句和表达式的区别。语句是以分号结尾且 不会计算出某个值 的代码行。另一方面，表达式是一行不以分号结尾且计算出某个值的代码行。来让我们用一个例子来理解：fn main() { let a = 873; let b = { // 语句 println!("Assigning some value to b..."); // 表达式 a * 10 }; println!("b: {b}"); } 在第 3 行，我开始了一个代码块，在这个代码块中我有一个语句和一个表达式。注释标明了哪个是哪个。在第 5 行的代码不会计算出某个值，因此需要以分号结尾。这是一个语句。第 8 行的代码计算出了一个值。它是 a * 10（873 * 10），并计算出了 8730。因为这一行没有以分号结尾，所以这是一个表达式。? 使用表达式是从代码块中返回某些东西的一种方便的方法。因此，当返回一个值时，它是 return 关键字的替代方案。表达式不仅仅用于从函数中 “返回” 一个值。正如你刚刚看到的，a * 10 的值是从内部作用域 “返回” 到外部作用域，并赋值给变量 b。一个简单的作用域不是一个函数，但表达式的值仍然被 “返回” 了。示例：购买腐烂的水果来让我们看一个演示以理解函数如何返回一个值：fn main() { println!( "如果我从水果摊买了 2 公斤苹果，我必须付给他们 {} 印度卢比。", retail_price(2.0) ); println!( "但是，如果我从水果摊买了 30 公斤苹果，我就要付给他们 {} 印度卢比。", wholesale_pri…

Rust 基础系列 #4
type: item
date: 2023-05-24T00:57:19+08:00
description: 在 Rust 系列的第四篇中，学习复合数据类型、数组和元组。在上一篇文章中，你学习到了 Rust 中的 标量数据类型。它们是整型、浮点数、字符和布尔值。在本文中，我们将会看看 Rust 编程语言中的复合数据类型。Rust 中的复合数据类型是什么？复合数据类型可以在一个变量中存储多个值。这些值可以是相同的标量数据类型，也可以是不同的标量数据类型。Rust 编程语言中有两种这样的数据类型： 数组 Array ：存储相同类型的多个值。 元组 Tuple ：存储多个值，可以是相同的类型，也可以是不同的类型。让我们了解一下它们吧！Rust 中的数组Rust 编程语言中的数组具有以下特性：每一个元素都必须是相同的类型数组有一个固定的长度数组存储在堆栈中，即其中存储的数据可以被 迅速 访问创建数组的语法如下：// 无类型声明 let variable_name = [element1, element2, ..., elementn]; // 有类型声明 let variable_name: [data_type; array_length] = [element1, element2, ..., elementn]; 数组中的元素是在方括号中声明的。要访问数组的元素，需要在方括号中指定要访问的索引。来让我们看一个例子来更好地理解这个。fn main() { // 无类型声明 let greeting = ['H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!']; // 有类型声明 let pi: [i32; 10] = [1, 4, 1, 5, 9, 2, 6, 5, 3, 5]; for character in greeting { print!("{}", character); } println!("\nPi: 3.1{}{}{}{}", pi[0], pi[1], pi[2], pi[3]); } 这里，我定义了一个字符数组和另一个存储 i32 类型的值的数组。greeting 数组以单独字符的形式存储了字符串 "Hello world!" 的字符。pi 数组以单独数字的形式存储了圆周率小数点后的前 10 位数字。然后，我使用 for 循环打印了 greeting 数组的每个字符。（我很快就会讲到循环。）然后，我打印了 pi 数组的前 4 个值。Hello world! Pi: 3.11415 如果你想创建一个数组，其中每个元素都是 y，并且出现 x 次，你可以使用以下快捷方式在 Rust 中实现：let variable_name = [y; x]; 来看一个演示……fn main() { let a = [10; 5]; for i in a { print!("{i} "); } println!(""); } 我创建了一个变量 a，它的长度为 5。数组中的每个元素都是 '10'。我通过使用 for 循环打印数组的每个元素来验证这一点。它的输出如下：10 10 10 10 10 ? 作为练习，尝试创建一个长度为 x 的数组，然后尝试访问数组的第 x+1 个元素。看看会发生什么。Rust 中的元组Rust 中的元组具有以下特性：就像数组一样，元组的长度是固定的元素可以是相同的/不同的标量数据类型元组存储在堆栈中，所以访问速度更快创建元组的语法如下：// 无类型声明 let variable_name = (element1, element2, ..., element3); // 有类型声明 let variable_name: (data_type, ..., data_type) = (element1, element2, ..., element3); 元组的元素写在圆括号中。要访问元素，使用点运算符，后跟该元素的索引。fn main() { let a = (38, 923.329, true); let b: (char, i32, f64, bool) = ('r', 43, 3.14, false); println!("a.0: {}, a.1: {}, a.2: {}", a.0, a.1, a.2); println!("b.0: {}, b.1: {}, b.2: {}, b.3: {}", b.0, b.1, b.2, b.3); // 元组解构 let pixel = (50, 0, 200); let (red, green, blue) = pixel; println!("red: {}, green: {}, blue: {}", red, green, blue); } 在上面的代码中，我在第 2 行和第 3 行声明了两…

Rust 基础系列 #3
type: item
date: 2023-05-14T05:53:36+08:00
description: 在 上一篇 关于 Rust 编程语言的文章中，我们提到了变量、常量和 遮蔽 shadowing 。现在来讲解数据类型是再自然不过的了。数据类型是什么？将这个词汇展开些单词的顺序改变一下你就会得到答案了；“数据类型” -> “数据的类型”。计算机使用 0 和 1 来存储数据，但是为了让数据在读取时有意义，我们使用数据类型来表示这些 0 和 1 的含义。Rust 有两种数据类型：标量数据类型：只能存储单个值的类型。复合数据类型：可以存储多个值，甚至是不同类型的值。在本文中，我将讲解标量数据类型。我将在下一篇文章中讲解第二类数据类型。接下来是 Rust 中四种主要标量数据类型的简要概述：整型：存储整数。有每种特定情况下使用的子类型。浮点数：存储带有小数部分的数字。有两种基于大小的子类型。字符：使用 UTF-8 编码存储单个字符。（是的，你可以在字符中存储表情符号*。）布尔值： 存储 true 或 false。（给那些无法就 0 是 true 还是 0 是 false 达成一致的开发者。）整型在编程语言中，整型指的是一个整数。Rust 中的整型要么是有符号的，要么是无符号的。无符号整型只能存储 0 和正数，而有符号整型可以存储负数、0 和正数。? 一个有符号整型的范围从 -(2 n-1) 开始，以 (2 n-1)-1 结束。同样，无符号整型的范围从 0 开始，以 (2 n)-1 结束。这是根据符号和长度可用的整型：正如你所见，Rust 有 8、16、32、64 甚至 128 位的有符号和无符号整型！使用 *size 的整型根据计算机的架构而变化。在 8 位微控制器上，它是 *8，在 32 位的旧计算机上，它是 *32，在现代 64 位系统上，它是 *64。使用 *size 是为了存储与内存（这与裸机相关）有关的数据，比如指针、偏移量等。? 当你没有显式地指定整型的子类型时，Rust 编译器会默认推断为 i32。显然，如果值比 i32 能存储的值大或小，Rust 编译器会礼貌地报错并要求你手动指定类型。Rust 不仅允许你以十进制形式存储整数，还允许你以二进制、八进制和十六进制形式存储整数。为了更好的可读性，你可以使用下划线 _ 来代替逗号来书写/读取大数。fn main() { let bin_value = 0b100_0101; // 使用前缀“0b”表示二进制 let oct_value = 0o105; // 使用前缀“0o”表示八进制 let hex_value = 0x45; // 使用前缀“0x”表示十六进制 let dec_value = 1_00_00_000; // 和写一克若（1,00,00,000）一样 println!("二进制值: {bin_value}"); println!("八进制值: {oct_value}"); println!("十六进制值: {hex_value}"); println!("十进制值: {dec_value}"); } 我使用二进制、八进制和十六进制分别将十进制数 69 存储在变量 bin_value、oct_value 和 hex_value 中。在变量 dec_value 中，我存储了数字 1 克若 1 Crore （一千万），并且使用了下划线替代逗号，这是印度的书写系统。对于那些更熟悉国际计数系统的人来说，你可以将其写成 10_000_000。在编译并运行这个二进制文件后，我得到了如下输出：二进制值: 69 八进制值: 69 十六进制值: 69 十进制值: 10000000 浮点数浮点数是一种存储带有小数部分的数字的数据类型。与 Rust 中的整型不同，浮点数只有两种子类型：f32: 单精度浮点数类型f64: 双精度浮点数类型和 Rust 中的整型一样，当 Rust 推断一个变量的类型时，如果它看起来像一个浮点数，那么它就会被赋予 f64 类型。这是因为 f64 类型比 f32 类型有更高的精度，并且在大多数计算操作中几乎和 f32 类型一样快。请注意，浮点数据类型（f32 和 f64）都是有符号的。? Rust 编程语言按照 IEEE 754 二进制浮点数表示与算术标准存储浮点数。fn main() { let pi: f32 = 3.1400; // f32 let golden_ratio = 1.610000; // f64 let five = 5.00; // 小数点表示它必须被推断为浮点数 let six: f64 = 6.; // 尽管类型说明被显式的添加了，小数点也是**必须**的 println!("pi: {pi}"); println!("黄金比例: {golden_ratio}"); println!("五: {five}"); println!…