162306a36Sopenharmony_ci.. SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci.. include:: ../disclaimer-zh_CN.rst 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci:Original: Documentation/rust/coding-guidelines.rst 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci:翻译: 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci 司延腾 Yanteng Si <siyanteng@loongson.cn> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci编码指南 1162306a36Sopenharmony_ci======== 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci本文档描述了如何在内核中编写Rust代码。 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci风格和格式化 1762306a36Sopenharmony_ci------------ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci代码应该使用 ``rustfmt`` 进行格式化。这样一来,一个不时为内核做贡献的人就不需要再去学 2062306a36Sopenharmony_ci习和记忆一个样式指南了。更重要的是,审阅者和维护者不需要再花时间指出风格问题,这样就可以 2162306a36Sopenharmony_ci减少补丁落地所需的邮件往返。 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci.. note:: ``rustfmt`` 不检查注释和文档的约定。因此,这些仍然需要照顾到。 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci使用 ``rustfmt`` 的默认设置。这意味着遵循Rust的习惯性风格。例如,缩进时使用4个空格而 2662306a36Sopenharmony_ci不是制表符。 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci在输入、保存或提交时告知编辑器/IDE进行格式化是很方便的。然而,如果因为某些原因需要在某 2962306a36Sopenharmony_ci个时候重新格式化整个内核Rust的源代码,可以运行以下程序:: 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci make LLVM=1 rustfmt 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci也可以检查所有的东西是否都是格式化的(否则就打印一个差异),例如对于一个CI,用:: 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci make LLVM=1 rustfmtcheck 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci像内核其他部分的 ``clang-format`` 一样, ``rustfmt`` 在单个文件上工作,并且不需要 3862306a36Sopenharmony_ci内核配置。有时,它甚至可以与破碎的代码一起工作。 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci注释 4262306a36Sopenharmony_ci---- 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci“普通”注释(即以 ``//`` 开头,而不是 ``///`` 或 ``//!`` 开头的代码文档)的写法与文 4562306a36Sopenharmony_ci档注释相同,使用Markdown语法,尽管它们不会被渲染。这提高了一致性,简化了规则,并允许在 4662306a36Sopenharmony_ci这两种注释之间更容易地移动内容。比如说: 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci.. code-block:: rust 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci // `object` is ready to be handled now. 5162306a36Sopenharmony_ci f(object); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci此外,就像文档一样,注释在句子的开头要大写,并以句号结束(即使是单句)。这包括 ``// SAFETY:``, 5462306a36Sopenharmony_ci``// TODO:`` 和其他“标记”的注释,例如: 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci.. code-block:: rust 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci // FIXME: The error should be handled properly. 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci注释不应该被用于文档的目的:注释是为了实现细节,而不是为了用户。即使源文件的读者既是API 6162306a36Sopenharmony_ci的实现者又是用户,这种区分也是有用的。事实上,有时同时使用注释和文档是很有用的。例如,用 6262306a36Sopenharmony_ci于 ``TODO`` 列表或对文档本身的注释。对于后一种情况,注释可以插在中间;也就是说,离要注 6362306a36Sopenharmony_ci释的文档行更近。对于其他情况,注释会写在文档之后,例如: 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci.. code-block:: rust 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /// Returns a new [`Foo`]. 6862306a36Sopenharmony_ci /// 6962306a36Sopenharmony_ci /// # Examples 7062306a36Sopenharmony_ci /// 7162306a36Sopenharmony_ci // TODO: Find a better example. 7262306a36Sopenharmony_ci /// ``` 7362306a36Sopenharmony_ci /// let foo = f(42); 7462306a36Sopenharmony_ci /// ``` 7562306a36Sopenharmony_ci // FIXME: Use fallible approach. 7662306a36Sopenharmony_ci pub fn f(x: i32) -> Foo { 7762306a36Sopenharmony_ci // ... 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci一种特殊的注释是 ``// SAFETY:`` 注释。这些注释必须出现在每个 ``unsafe`` 块之前,它们 8162306a36Sopenharmony_ci解释了为什么该块内的代码是正确/健全的,即为什么它在任何情况下都不会触发未定义行为,例如: 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci.. code-block:: rust 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci // SAFETY: `p` is valid by the safety requirements. 8662306a36Sopenharmony_ci unsafe { *p = 0; } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci``// SAFETY:`` 注释不能与代码文档中的 ``# Safety`` 部分相混淆。 ``# Safety`` 部 8962306a36Sopenharmony_ci分指定了(函数)调用者或(特性)实现者需要遵守的契约。 9062306a36Sopenharmony_ci``// SAFETY:`` 注释显示了为什么一个(函数)调用者或(特性)实现者实际上尊重了 9162306a36Sopenharmony_ci``# Safety`` 部分或语言参考中的前提条件。 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci代码文档 9562306a36Sopenharmony_ci-------- 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciRust内核代码不像C内核代码那样被记录下来(即通过kernel-doc)。取而代之的是用于记录Rust 9862306a36Sopenharmony_ci代码的常用系统:rustdoc工具,它使用Markdown(一种轻量级的标记语言)。 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci要学习Markdown,外面有很多指南。例如: 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cihttps://commonmark.org/help/ 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci一个记录良好的Rust函数可能是这样的: 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci.. code-block:: rust 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /// Returns the contained [`Some`] value, consuming the `self` value, 10962306a36Sopenharmony_ci /// without checking that the value is not [`None`]. 11062306a36Sopenharmony_ci /// 11162306a36Sopenharmony_ci /// # Safety 11262306a36Sopenharmony_ci /// 11362306a36Sopenharmony_ci /// Calling this method on [`None`] is *[undefined behavior]*. 11462306a36Sopenharmony_ci /// 11562306a36Sopenharmony_ci /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html 11662306a36Sopenharmony_ci /// 11762306a36Sopenharmony_ci /// # Examples 11862306a36Sopenharmony_ci /// 11962306a36Sopenharmony_ci /// ``` 12062306a36Sopenharmony_ci /// let x = Some("air"); 12162306a36Sopenharmony_ci /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); 12262306a36Sopenharmony_ci /// ``` 12362306a36Sopenharmony_ci pub unsafe fn unwrap_unchecked(self) -> T { 12462306a36Sopenharmony_ci match self { 12562306a36Sopenharmony_ci Some(val) => val, 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci // SAFETY: The safety contract must be upheld by the caller. 12862306a36Sopenharmony_ci None => unsafe { hint::unreachable_unchecked() }, 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci这个例子展示了一些 ``rustdoc`` 的特性和内核中遵循的一些惯例: 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci - 第一段必须是一个简单的句子,简要地描述被记录的项目的作用。进一步的解释必须放在额 13562306a36Sopenharmony_ci 外的段落中。 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci - 不安全的函数必须在 ``# Safety`` 部分记录其安全前提条件。 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci - 虽然这里没有显示,但如果一个函数可能会恐慌,那么必须在 ``# Panics`` 部分描述发 14062306a36Sopenharmony_ci 生这种情况的条件。 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci 请注意,恐慌应该是非常少见的,只有在有充分理由的情况下才会使用。几乎在所有的情况下, 14362306a36Sopenharmony_ci 都应该使用一个可失败的方法,通常是返回一个 ``Result``。 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci - 如果提供使用实例对读者有帮助的话,必须写在一个叫做``# Examples``的部分。 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci - Rust项目(函数、类型、常量……)必须有适当的链接(``rustdoc`` 会自动创建一个 14862306a36Sopenharmony_ci 链接)。 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci - 任何 ``unsafe`` 的代码块都必须在前面加上一个 ``// SAFETY:`` 的注释,描述里面 15162306a36Sopenharmony_ci 的代码为什么是正确的。 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci 虽然有时原因可能看起来微不足道,但写这些注释不仅是记录已经考虑到的问题的好方法, 15462306a36Sopenharmony_ci 最重要的是,它提供了一种知道没有额外隐含约束的方法。 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci要了解更多关于如何编写Rust和拓展功能的文档,请看看 ``rustdoc`` 这本书,网址是: 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci命名 16262306a36Sopenharmony_ci---- 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ciRust内核代码遵循通常的Rust命名空间: 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci https://rust-lang.github.io/api-guidelines/naming.html 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci当现有的C语言概念(如宏、函数、对象......)被包装成Rust抽象时,应该使用尽可能接近C语 16962306a36Sopenharmony_ci言的名称,以避免混淆,并在C语言和Rust语言之间来回切换时提高可读性。例如,C语言中的 17062306a36Sopenharmony_ci``pr_info`` 这样的宏在Rust中的命名是一样的。 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci说到这里,应该调整大小写以遵循Rust的命名惯例,模块和类型引入的命名间隔不应该在项目名称 17362306a36Sopenharmony_ci中重复。例如,在包装常量时,如: 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci.. code-block:: c 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci #define GPIO_LINE_DIRECTION_IN 0 17862306a36Sopenharmony_ci #define GPIO_LINE_DIRECTION_OUT 1 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci在Rust中的等价物可能是这样的(忽略文档)。: 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci.. code-block:: rust 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci pub mod gpio { 18562306a36Sopenharmony_ci pub enum LineDirection { 18662306a36Sopenharmony_ci In = bindings::GPIO_LINE_DIRECTION_IN as _, 18762306a36Sopenharmony_ci Out = bindings::GPIO_LINE_DIRECTION_OUT as _, 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci也就是说, ``GPIO_LINE_DIRECTION_IN`` 的等价物将被称为 ``gpio::LineDirection::In`` 。 19262306a36Sopenharmony_ci特别是,它不应该被命名为 ``gpio::gpio_line_direction::GPIO_LINE_DIRECTION_IN`` 。 193