19b522a85SYanteng Si.. SPDX-License-Identifier: GPL-2.0 29b522a85SYanteng Si.. include:: ../disclaimer-zh_CN.rst 39b522a85SYanteng Si 49b522a85SYanteng Si:Original: Documentation/rust/coding-guidelines.rst 59b522a85SYanteng Si 69b522a85SYanteng Si:翻译: 79b522a85SYanteng Si 89b522a85SYanteng Si 司延腾 Yanteng Si <siyanteng@loongson.cn> 99b522a85SYanteng Si 109b522a85SYanteng Si编码指南 119b522a85SYanteng Si======== 129b522a85SYanteng Si 139b522a85SYanteng Si本文档描述了如何在内核中编写Rust代码。 149b522a85SYanteng Si 159b522a85SYanteng Si 169b522a85SYanteng Si风格和格式化 179b522a85SYanteng Si------------ 189b522a85SYanteng Si 199b522a85SYanteng Si代码应该使用 ``rustfmt`` 进行格式化。这样一来,一个不时为内核做贡献的人就不需要再去学 209b522a85SYanteng Si习和记忆一个样式指南了。更重要的是,审阅者和维护者不需要再花时间指出风格问题,这样就可以 219b522a85SYanteng Si减少补丁落地所需的邮件往返。 229b522a85SYanteng Si 239b522a85SYanteng Si.. note:: ``rustfmt`` 不检查注释和文档的约定。因此,这些仍然需要照顾到。 249b522a85SYanteng Si 259b522a85SYanteng Si使用 ``rustfmt`` 的默认设置。这意味着遵循Rust的习惯性风格。例如,缩进时使用4个空格而 269b522a85SYanteng Si不是制表符。 279b522a85SYanteng Si 289b522a85SYanteng Si在输入、保存或提交时告知编辑器/IDE进行格式化是很方便的。然而,如果因为某些原因需要在某 299b522a85SYanteng Si个时候重新格式化整个内核Rust的源代码,可以运行以下程序:: 309b522a85SYanteng Si 319b522a85SYanteng Si make LLVM=1 rustfmt 329b522a85SYanteng Si 339b522a85SYanteng Si也可以检查所有的东西是否都是格式化的(否则就打印一个差异),例如对于一个CI,用:: 349b522a85SYanteng Si 359b522a85SYanteng Si make LLVM=1 rustfmtcheck 369b522a85SYanteng Si 379b522a85SYanteng Si像内核其他部分的 ``clang-format`` 一样, ``rustfmt`` 在单个文件上工作,并且不需要 389b522a85SYanteng Si内核配置。有时,它甚至可以与破碎的代码一起工作。 399b522a85SYanteng Si 409b522a85SYanteng Si 419b522a85SYanteng Si注释 429b522a85SYanteng Si---- 439b522a85SYanteng Si 449b522a85SYanteng Si“普通”注释(即以 ``//`` 开头,而不是 ``///`` 或 ``//!`` 开头的代码文档)的写法与文 459b522a85SYanteng Si档注释相同,使用Markdown语法,尽管它们不会被渲染。这提高了一致性,简化了规则,并允许在 469b522a85SYanteng Si这两种注释之间更容易地移动内容。比如说: 479b522a85SYanteng Si 489b522a85SYanteng Si.. code-block:: rust 499b522a85SYanteng Si 509b522a85SYanteng Si // `object` is ready to be handled now. 519b522a85SYanteng Si f(object); 529b522a85SYanteng Si 539b522a85SYanteng Si此外,就像文档一样,注释在句子的开头要大写,并以句号结束(即使是单句)。这包括 ``// SAFETY:``, 549b522a85SYanteng Si``// TODO:`` 和其他“标记”的注释,例如: 559b522a85SYanteng Si 569b522a85SYanteng Si.. code-block:: rust 579b522a85SYanteng Si 589b522a85SYanteng Si // FIXME: The error should be handled properly. 599b522a85SYanteng Si 609b522a85SYanteng Si注释不应该被用于文档的目的:注释是为了实现细节,而不是为了用户。即使源文件的读者既是API 619b522a85SYanteng Si的实现者又是用户,这种区分也是有用的。事实上,有时同时使用注释和文档是很有用的。例如,用 629b522a85SYanteng Si于 ``TODO`` 列表或对文档本身的注释。对于后一种情况,注释可以插在中间;也就是说,离要注 639b522a85SYanteng Si释的文档行更近。对于其他情况,注释会写在文档之后,例如: 649b522a85SYanteng Si 659b522a85SYanteng Si.. code-block:: rust 669b522a85SYanteng Si 679b522a85SYanteng Si /// Returns a new [`Foo`]. 689b522a85SYanteng Si /// 699b522a85SYanteng Si /// # Examples 709b522a85SYanteng Si /// 719b522a85SYanteng Si // TODO: Find a better example. 729b522a85SYanteng Si /// ``` 739b522a85SYanteng Si /// let foo = f(42); 749b522a85SYanteng Si /// ``` 759b522a85SYanteng Si // FIXME: Use fallible approach. 769b522a85SYanteng Si pub fn f(x: i32) -> Foo { 779b522a85SYanteng Si // ... 789b522a85SYanteng Si } 799b522a85SYanteng Si 809b522a85SYanteng Si一种特殊的注释是 ``// SAFETY:`` 注释。这些注释必须出现在每个 ``unsafe`` 块之前,它们 819b522a85SYanteng Si解释了为什么该块内的代码是正确/健全的,即为什么它在任何情况下都不会触发未定义行为,例如: 829b522a85SYanteng Si 839b522a85SYanteng Si.. code-block:: rust 849b522a85SYanteng Si 859b522a85SYanteng Si // SAFETY: `p` is valid by the safety requirements. 869b522a85SYanteng Si unsafe { *p = 0; } 879b522a85SYanteng Si 889b522a85SYanteng Si``// SAFETY:`` 注释不能与代码文档中的 ``# Safety`` 部分相混淆。 ``# Safety`` 部 899b522a85SYanteng Si分指定了(函数)调用者或(特性)实现者需要遵守的契约。 909b522a85SYanteng Si``// SAFETY:`` 注释显示了为什么一个(函数)调用者或(特性)实现者实际上尊重了 919b522a85SYanteng Si``# Safety`` 部分或语言参考中的前提条件。 929b522a85SYanteng Si 939b522a85SYanteng Si 949b522a85SYanteng Si代码文档 959b522a85SYanteng Si-------- 969b522a85SYanteng Si 979b522a85SYanteng SiRust内核代码不像C内核代码那样被记录下来(即通过kernel-doc)。取而代之的是用于记录Rust 989b522a85SYanteng Si代码的常用系统:rustdoc工具,它使用Markdown(一种轻量级的标记语言)。 999b522a85SYanteng Si 1009b522a85SYanteng Si要学习Markdown,外面有很多指南。例如: 1019b522a85SYanteng Si 1029b522a85SYanteng Sihttps://commonmark.org/help/ 1039b522a85SYanteng Si 1049b522a85SYanteng Si一个记录良好的Rust函数可能是这样的: 1059b522a85SYanteng Si 1069b522a85SYanteng Si.. code-block:: rust 1079b522a85SYanteng Si 1089b522a85SYanteng Si /// Returns the contained [`Some`] value, consuming the `self` value, 1099b522a85SYanteng Si /// without checking that the value is not [`None`]. 1109b522a85SYanteng Si /// 1119b522a85SYanteng Si /// # Safety 1129b522a85SYanteng Si /// 1139b522a85SYanteng Si /// Calling this method on [`None`] is *[undefined behavior]*. 1149b522a85SYanteng Si /// 1159b522a85SYanteng Si /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html 1169b522a85SYanteng Si /// 1179b522a85SYanteng Si /// # Examples 1189b522a85SYanteng Si /// 1199b522a85SYanteng Si /// ``` 1209b522a85SYanteng Si /// let x = Some("air"); 1219b522a85SYanteng Si /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); 1229b522a85SYanteng Si /// ``` 1239b522a85SYanteng Si pub unsafe fn unwrap_unchecked(self) -> T { 1249b522a85SYanteng Si match self { 1259b522a85SYanteng Si Some(val) => val, 1269b522a85SYanteng Si 1279b522a85SYanteng Si // SAFETY: The safety contract must be upheld by the caller. 1289b522a85SYanteng Si None => unsafe { hint::unreachable_unchecked() }, 1299b522a85SYanteng Si } 1309b522a85SYanteng Si } 1319b522a85SYanteng Si 1329b522a85SYanteng Si这个例子展示了一些 ``rustdoc`` 的特性和内核中遵循的一些惯例: 1339b522a85SYanteng Si 1349b522a85SYanteng Si - 第一段必须是一个简单的句子,简要地描述被记录的项目的作用。进一步的解释必须放在额 1359b522a85SYanteng Si 外的段落中。 1369b522a85SYanteng Si 1379b522a85SYanteng Si - 不安全的函数必须在 ``# Safety`` 部分记录其安全前提条件。 1389b522a85SYanteng Si 1399b522a85SYanteng Si - 虽然这里没有显示,但如果一个函数可能会恐慌,那么必须在 ``# Panics`` 部分描述发 1409b522a85SYanteng Si 生这种情况的条件。 1419b522a85SYanteng Si 1429b522a85SYanteng Si 请注意,恐慌应该是非常少见的,只有在有充分理由的情况下才会使用。几乎在所有的情况下, 1439b522a85SYanteng Si 都应该使用一个可失败的方法,通常是返回一个 ``Result``。 1449b522a85SYanteng Si 1459b522a85SYanteng Si - 如果提供使用实例对读者有帮助的话,必须写在一个叫做``# Examples``的部分。 1469b522a85SYanteng Si 1479b522a85SYanteng Si - Rust项目(函数、类型、常量……)必须有适当的链接(``rustdoc`` 会自动创建一个 1489b522a85SYanteng Si 链接)。 1499b522a85SYanteng Si 1509b522a85SYanteng Si - 任何 ``unsafe`` 的代码块都必须在前面加上一个 ``// SAFETY:`` 的注释,描述里面 1519b522a85SYanteng Si 的代码为什么是正确的。 1529b522a85SYanteng Si 1539b522a85SYanteng Si 虽然有时原因可能看起来微不足道,但写这些注释不仅是记录已经考虑到的问题的好方法, 1549b522a85SYanteng Si 最重要的是,它提供了一种知道没有额外隐含约束的方法。 1559b522a85SYanteng Si 1569b522a85SYanteng Si要了解更多关于如何编写Rust和拓展功能的文档,请看看 ``rustdoc`` 这本书,网址是: 1579b522a85SYanteng Si 1589b522a85SYanteng Si https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html 1599b522a85SYanteng Si 160*88bfcfa4SYanteng Si此外,内核支持通过在链接目标前添加 ``srctree/`` 来创建相对于源代码树的链接。例如: 161*88bfcfa4SYanteng Si 162*88bfcfa4SYanteng Si.. code-block:: rust 163*88bfcfa4SYanteng Si 164*88bfcfa4SYanteng Si //! C header: [`include/linux/printk.h`](srctree/include/linux/printk.h) 165*88bfcfa4SYanteng Si 166*88bfcfa4SYanteng Si或者: 167*88bfcfa4SYanteng Si 168*88bfcfa4SYanteng Si.. code-block:: rust 169*88bfcfa4SYanteng Si 170*88bfcfa4SYanteng Si /// [`struct mutex`]: srctree/include/linux/mutex.h 171*88bfcfa4SYanteng Si 1729b522a85SYanteng Si 1739b522a85SYanteng Si命名 1749b522a85SYanteng Si---- 1759b522a85SYanteng Si 1769b522a85SYanteng SiRust内核代码遵循通常的Rust命名空间: 1779b522a85SYanteng Si 1789b522a85SYanteng Si https://rust-lang.github.io/api-guidelines/naming.html 1799b522a85SYanteng Si 1809b522a85SYanteng Si当现有的C语言概念(如宏、函数、对象......)被包装成Rust抽象时,应该使用尽可能接近C语 1819b522a85SYanteng Si言的名称,以避免混淆,并在C语言和Rust语言之间来回切换时提高可读性。例如,C语言中的 1829b522a85SYanteng Si``pr_info`` 这样的宏在Rust中的命名是一样的。 1839b522a85SYanteng Si 1849b522a85SYanteng Si说到这里,应该调整大小写以遵循Rust的命名惯例,模块和类型引入的命名间隔不应该在项目名称 1859b522a85SYanteng Si中重复。例如,在包装常量时,如: 1869b522a85SYanteng Si 1879b522a85SYanteng Si.. code-block:: c 1889b522a85SYanteng Si 1899b522a85SYanteng Si #define GPIO_LINE_DIRECTION_IN 0 1909b522a85SYanteng Si #define GPIO_LINE_DIRECTION_OUT 1 1919b522a85SYanteng Si 1929b522a85SYanteng Si在Rust中的等价物可能是这样的(忽略文档)。: 1939b522a85SYanteng Si 1949b522a85SYanteng Si.. code-block:: rust 1959b522a85SYanteng Si 1969b522a85SYanteng Si pub mod gpio { 1979b522a85SYanteng Si pub enum LineDirection { 1989b522a85SYanteng Si In = bindings::GPIO_LINE_DIRECTION_IN as _, 1999b522a85SYanteng Si Out = bindings::GPIO_LINE_DIRECTION_OUT as _, 2009b522a85SYanteng Si } 2019b522a85SYanteng Si } 2029b522a85SYanteng Si 2039b522a85SYanteng Si也就是说, ``GPIO_LINE_DIRECTION_IN`` 的等价物将被称为 ``gpio::LineDirection::In`` 。 2049b522a85SYanteng Si特别是,它不应该被命名为 ``gpio::gpio_line_direction::GPIO_LINE_DIRECTION_IN`` 。 205