xref: /linux/Documentation/translations/zh_CN/rust/coding-guidelines.rst (revision 9b522a8582f8e00f37d6ec4eabf5edaac2cf6e9c)
1*9b522a85SYanteng Si.. SPDX-License-Identifier: GPL-2.0
2*9b522a85SYanteng Si.. include:: ../disclaimer-zh_CN.rst
3*9b522a85SYanteng Si
4*9b522a85SYanteng Si:Original: Documentation/rust/coding-guidelines.rst
5*9b522a85SYanteng Si
6*9b522a85SYanteng Si:翻译:
7*9b522a85SYanteng Si
8*9b522a85SYanteng Si 司延腾 Yanteng Si <siyanteng@loongson.cn>
9*9b522a85SYanteng Si
10*9b522a85SYanteng Si编码指南
11*9b522a85SYanteng Si========
12*9b522a85SYanteng Si
13*9b522a85SYanteng Si本文档描述了如何在内核中编写Rust代码。
14*9b522a85SYanteng Si
15*9b522a85SYanteng Si
16*9b522a85SYanteng Si风格和格式化
17*9b522a85SYanteng Si------------
18*9b522a85SYanteng Si
19*9b522a85SYanteng Si代码应该使用 ``rustfmt`` 进行格式化。这样一来,一个不时为内核做贡献的人就不需要再去学
20*9b522a85SYanteng Si习和记忆一个样式指南了。更重要的是,审阅者和维护者不需要再花时间指出风格问题,这样就可以
21*9b522a85SYanteng Si减少补丁落地所需的邮件往返。
22*9b522a85SYanteng Si
23*9b522a85SYanteng Si.. note::  ``rustfmt`` 不检查注释和文档的约定。因此,这些仍然需要照顾到。
24*9b522a85SYanteng Si
25*9b522a85SYanteng Si使用 ``rustfmt`` 的默认设置。这意味着遵循Rust的习惯性风格。例如,缩进时使用4个空格而
26*9b522a85SYanteng Si不是制表符。
27*9b522a85SYanteng Si
28*9b522a85SYanteng Si在输入、保存或提交时告知编辑器/IDE进行格式化是很方便的。然而,如果因为某些原因需要在某
29*9b522a85SYanteng Si个时候重新格式化整个内核Rust的源代码,可以运行以下程序::
30*9b522a85SYanteng Si
31*9b522a85SYanteng Si	make LLVM=1 rustfmt
32*9b522a85SYanteng Si
33*9b522a85SYanteng Si也可以检查所有的东西是否都是格式化的(否则就打印一个差异),例如对于一个CI,用::
34*9b522a85SYanteng Si
35*9b522a85SYanteng Si	make LLVM=1 rustfmtcheck
36*9b522a85SYanteng Si
37*9b522a85SYanteng Si像内核其他部分的 ``clang-format`` 一样, ``rustfmt`` 在单个文件上工作,并且不需要
38*9b522a85SYanteng Si内核配置。有时,它甚至可以与破碎的代码一起工作。
39*9b522a85SYanteng Si
40*9b522a85SYanteng Si
41*9b522a85SYanteng Si注释
42*9b522a85SYanteng Si----
43*9b522a85SYanteng Si
44*9b522a85SYanteng Si“普通”注释(即以 ``//`` 开头,而不是 ``///`` 或 ``//!`` 开头的代码文档)的写法与文
45*9b522a85SYanteng Si档注释相同,使用Markdown语法,尽管它们不会被渲染。这提高了一致性,简化了规则,并允许在
46*9b522a85SYanteng Si这两种注释之间更容易地移动内容。比如说:
47*9b522a85SYanteng Si
48*9b522a85SYanteng Si.. code-block:: rust
49*9b522a85SYanteng Si
50*9b522a85SYanteng Si	// `object` is ready to be handled now.
51*9b522a85SYanteng Si	f(object);
52*9b522a85SYanteng Si
53*9b522a85SYanteng Si此外,就像文档一样,注释在句子的开头要大写,并以句号结束(即使是单句)。这包括 ``// SAFETY:``,
54*9b522a85SYanteng Si``// TODO:`` 和其他“标记”的注释,例如:
55*9b522a85SYanteng Si
56*9b522a85SYanteng Si.. code-block:: rust
57*9b522a85SYanteng Si
58*9b522a85SYanteng Si	// FIXME: The error should be handled properly.
59*9b522a85SYanteng Si
60*9b522a85SYanteng Si注释不应该被用于文档的目的:注释是为了实现细节,而不是为了用户。即使源文件的读者既是API
61*9b522a85SYanteng Si的实现者又是用户,这种区分也是有用的。事实上,有时同时使用注释和文档是很有用的。例如,用
62*9b522a85SYanteng Si于 ``TODO`` 列表或对文档本身的注释。对于后一种情况,注释可以插在中间;也就是说,离要注
63*9b522a85SYanteng Si释的文档行更近。对于其他情况,注释会写在文档之后,例如:
64*9b522a85SYanteng Si
65*9b522a85SYanteng Si.. code-block:: rust
66*9b522a85SYanteng Si
67*9b522a85SYanteng Si	/// Returns a new [`Foo`].
68*9b522a85SYanteng Si	///
69*9b522a85SYanteng Si	/// # Examples
70*9b522a85SYanteng Si	///
71*9b522a85SYanteng Si	// TODO: Find a better example.
72*9b522a85SYanteng Si	/// ```
73*9b522a85SYanteng Si	/// let foo = f(42);
74*9b522a85SYanteng Si	/// ```
75*9b522a85SYanteng Si	// FIXME: Use fallible approach.
76*9b522a85SYanteng Si	pub fn f(x: i32) -> Foo {
77*9b522a85SYanteng Si	    // ...
78*9b522a85SYanteng Si	}
79*9b522a85SYanteng Si
80*9b522a85SYanteng Si一种特殊的注释是 ``// SAFETY:`` 注释。这些注释必须出现在每个 ``unsafe`` 块之前,它们
81*9b522a85SYanteng Si解释了为什么该块内的代码是正确/健全的,即为什么它在任何情况下都不会触发未定义行为,例如:
82*9b522a85SYanteng Si
83*9b522a85SYanteng Si.. code-block:: rust
84*9b522a85SYanteng Si
85*9b522a85SYanteng Si	// SAFETY: `p` is valid by the safety requirements.
86*9b522a85SYanteng Si	unsafe { *p = 0; }
87*9b522a85SYanteng Si
88*9b522a85SYanteng Si``// SAFETY:`` 注释不能与代码文档中的 ``# Safety`` 部分相混淆。 ``# Safety`` 部
89*9b522a85SYanteng Si分指定了(函数)调用者或(特性)实现者需要遵守的契约。
90*9b522a85SYanteng Si``// SAFETY:`` 注释显示了为什么一个(函数)调用者或(特性)实现者实际上尊重了
91*9b522a85SYanteng Si``# Safety`` 部分或语言参考中的前提条件。
92*9b522a85SYanteng Si
93*9b522a85SYanteng Si
94*9b522a85SYanteng Si代码文档
95*9b522a85SYanteng Si--------
96*9b522a85SYanteng Si
97*9b522a85SYanteng SiRust内核代码不像C内核代码那样被记录下来(即通过kernel-doc)。取而代之的是用于记录Rust
98*9b522a85SYanteng Si代码的常用系统:rustdoc工具,它使用Markdown(一种轻量级的标记语言)。
99*9b522a85SYanteng Si
100*9b522a85SYanteng Si要学习Markdown,外面有很多指南。例如:
101*9b522a85SYanteng Si
102*9b522a85SYanteng Sihttps://commonmark.org/help/
103*9b522a85SYanteng Si
104*9b522a85SYanteng Si一个记录良好的Rust函数可能是这样的:
105*9b522a85SYanteng Si
106*9b522a85SYanteng Si.. code-block:: rust
107*9b522a85SYanteng Si
108*9b522a85SYanteng Si	/// Returns the contained [`Some`] value, consuming the `self` value,
109*9b522a85SYanteng Si	/// without checking that the value is not [`None`].
110*9b522a85SYanteng Si	///
111*9b522a85SYanteng Si	/// # Safety
112*9b522a85SYanteng Si	///
113*9b522a85SYanteng Si	/// Calling this method on [`None`] is *[undefined behavior]*.
114*9b522a85SYanteng Si	///
115*9b522a85SYanteng Si	/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
116*9b522a85SYanteng Si	///
117*9b522a85SYanteng Si	/// # Examples
118*9b522a85SYanteng Si	///
119*9b522a85SYanteng Si	/// ```
120*9b522a85SYanteng Si	/// let x = Some("air");
121*9b522a85SYanteng Si	/// assert_eq!(unsafe { x.unwrap_unchecked() }, "air");
122*9b522a85SYanteng Si	/// ```
123*9b522a85SYanteng Si	pub unsafe fn unwrap_unchecked(self) -> T {
124*9b522a85SYanteng Si	    match self {
125*9b522a85SYanteng Si	        Some(val) => val,
126*9b522a85SYanteng Si
127*9b522a85SYanteng Si	        // SAFETY: The safety contract must be upheld by the caller.
128*9b522a85SYanteng Si	        None => unsafe { hint::unreachable_unchecked() },
129*9b522a85SYanteng Si	    }
130*9b522a85SYanteng Si	}
131*9b522a85SYanteng Si
132*9b522a85SYanteng Si这个例子展示了一些 ``rustdoc`` 的特性和内核中遵循的一些惯例:
133*9b522a85SYanteng Si
134*9b522a85SYanteng Si  - 第一段必须是一个简单的句子,简要地描述被记录的项目的作用。进一步的解释必须放在额
135*9b522a85SYanteng Si    外的段落中。
136*9b522a85SYanteng Si
137*9b522a85SYanteng Si  - 不安全的函数必须在 ``# Safety`` 部分记录其安全前提条件。
138*9b522a85SYanteng Si
139*9b522a85SYanteng Si  - 虽然这里没有显示,但如果一个函数可能会恐慌,那么必须在 ``# Panics`` 部分描述发
140*9b522a85SYanteng Si    生这种情况的条件。
141*9b522a85SYanteng Si
142*9b522a85SYanteng Si    请注意,恐慌应该是非常少见的,只有在有充分理由的情况下才会使用。几乎在所有的情况下,
143*9b522a85SYanteng Si    都应该使用一个可失败的方法,通常是返回一个 ``Result``。
144*9b522a85SYanteng Si
145*9b522a85SYanteng Si  - 如果提供使用实例对读者有帮助的话,必须写在一个叫做``# Examples``的部分。
146*9b522a85SYanteng Si
147*9b522a85SYanteng Si  - Rust项目(函数、类型、常量……)必须有适当的链接(``rustdoc`` 会自动创建一个
148*9b522a85SYanteng Si    链接)。
149*9b522a85SYanteng Si
150*9b522a85SYanteng Si  - 任何 ``unsafe`` 的代码块都必须在前面加上一个 ``// SAFETY:`` 的注释,描述里面
151*9b522a85SYanteng Si    的代码为什么是正确的。
152*9b522a85SYanteng Si
153*9b522a85SYanteng Si    虽然有时原因可能看起来微不足道,但写这些注释不仅是记录已经考虑到的问题的好方法,
154*9b522a85SYanteng Si    最重要的是,它提供了一种知道没有额外隐含约束的方法。
155*9b522a85SYanteng Si
156*9b522a85SYanteng Si要了解更多关于如何编写Rust和拓展功能的文档,请看看 ``rustdoc`` 这本书,网址是:
157*9b522a85SYanteng Si
158*9b522a85SYanteng Si	https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html
159*9b522a85SYanteng Si
160*9b522a85SYanteng Si
161*9b522a85SYanteng Si命名
162*9b522a85SYanteng Si----
163*9b522a85SYanteng Si
164*9b522a85SYanteng SiRust内核代码遵循通常的Rust命名空间:
165*9b522a85SYanteng Si
166*9b522a85SYanteng Si	https://rust-lang.github.io/api-guidelines/naming.html
167*9b522a85SYanteng Si
168*9b522a85SYanteng Si当现有的C语言概念(如宏、函数、对象......)被包装成Rust抽象时,应该使用尽可能接近C语
169*9b522a85SYanteng Si言的名称,以避免混淆,并在C语言和Rust语言之间来回切换时提高可读性。例如,C语言中的
170*9b522a85SYanteng Si``pr_info`` 这样的宏在Rust中的命名是一样的。
171*9b522a85SYanteng Si
172*9b522a85SYanteng Si说到这里,应该调整大小写以遵循Rust的命名惯例,模块和类型引入的命名间隔不应该在项目名称
173*9b522a85SYanteng Si中重复。例如,在包装常量时,如:
174*9b522a85SYanteng Si
175*9b522a85SYanteng Si.. code-block:: c
176*9b522a85SYanteng Si
177*9b522a85SYanteng Si	#define GPIO_LINE_DIRECTION_IN	0
178*9b522a85SYanteng Si	#define GPIO_LINE_DIRECTION_OUT	1
179*9b522a85SYanteng Si
180*9b522a85SYanteng Si在Rust中的等价物可能是这样的(忽略文档)。:
181*9b522a85SYanteng Si
182*9b522a85SYanteng Si.. code-block:: rust
183*9b522a85SYanteng Si
184*9b522a85SYanteng Si	pub mod gpio {
185*9b522a85SYanteng Si	    pub enum LineDirection {
186*9b522a85SYanteng Si	        In = bindings::GPIO_LINE_DIRECTION_IN as _,
187*9b522a85SYanteng Si	        Out = bindings::GPIO_LINE_DIRECTION_OUT as _,
188*9b522a85SYanteng Si	    }
189*9b522a85SYanteng Si	}
190*9b522a85SYanteng Si
191*9b522a85SYanteng Si也就是说, ``GPIO_LINE_DIRECTION_IN`` 的等价物将被称为 ``gpio::LineDirection::In`` 。
192*9b522a85SYanteng Si特别是,它不应该被命名为 ``gpio::gpio_line_direction::GPIO_LINE_DIRECTION_IN`` 。
193