요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
Coding Guidelines
=================
This document describes how to write Rust code in the kernel.
Style & formatting
------------------
The code should be formatted using ``rustfmt``. In this way, a person
contributing from time to time to the kernel does not need to learn and
remember one more style guide. More importantly, reviewers and maintainers
do not need to spend time pointing out style issues anymore, and thus
less patch roundtrips may be needed to land a change.
.. note:: Conventions on comments and documentation are not checked by
``rustfmt``. Thus those are still needed to be taken care of.
The default settings of ``rustfmt`` are used. This means the idiomatic Rust
style is followed. For instance, 4 spaces are used for indentation rather
than tabs.
It is convenient to instruct editors/IDEs to format while typing,
when saving or at commit time. However, if for some reason reformatting
the entire kernel Rust sources is needed at some point, the following can be
run::
make LLVM=1 rustfmt
It is also possible to check if everything is formatted (printing a diff
otherwise), for instance for a CI, with::
make LLVM=1 rustfmtcheck
Like ``clang-format`` for the rest of the kernel, ``rustfmt`` works on
individual files, and does not require a kernel configuration. Sometimes it may
even work with broken code.
Imports
~~~~~~~
``rustfmt``, by default, formats imports in a way that is prone to conflicts
while merging and rebasing, since in some cases it condenses several items into
the same line. For instance:
.. code-block:: rust
// Do not use this style.
use crate::{
example1,
example2::{example3, example4, example5},
example6, example7,
example8::example9,
};
Instead, the kernel uses a vertical layout that looks like this:
.. code-block:: rust
use crate::{
example1,
example2::{
example3,
example4,
example5, //
},
example6,
example7,
example8::example9, //
};
That is, each item goes into its own line, and braces are used as soon as there
is more than one item in a list.
The trailing empty comment allows to preserve this formatting. Not only that,
``rustfmt`` will actually reformat imports vertically when the empty comment is
added. That is, it is possible to easily reformat the original example into the
expected style by running ``rustfmt`` on an input like:
.. code-block:: rust
// Do not use this style.
use crate::{
example1,
example2::{example3, example4, example5, //
},
example6, example7,
example8::example9, //
};
The trailing empty comment works for nested imports, as shown above, as well as
for single item imports -- this can be useful to minimize diffs within patch
series:
.. code-block:: rust
use crate::{
example1, //
};
The trailing empty comment works in any of the lines within the braces, but it
is preferred to keep it in the last item, since it is reminiscent of the
trailing comma in other formatters. Sometimes it may be simpler to avoid moving
the comment several times within a patch series due to changes in the list.
There may be cases where exceptions may need to be made, i.e. none of this is
a hard rule. There is also code that is not migrated to this style yet, but
please do not introduce code in other styles.
Eventually, the goal is to get ``rustfmt`` to support this formatting style (or
a similar one) automatically in a stable release without requiring the trailing
empty comment. Thus, at some point, the goal is to remove those comments.
Comments
--------
"Normal" comments (i.e. ``//``, rather than code documentation which starts
with ``///`` or ``//!``) are written in Markdown the same way as documentation
comments are, even though they will not be rendered. This improves consistency,
simplifies the rules and allows to move content between the two kinds of
comments more easily. For instance:
.. code-block:: rust
// `object` is ready to be handled now.
f(object);
Furthermore, just like documentation, comments are capitalized at the beginning
of a sentence and ended with a period (even if it is a single sentence). This
includes ``// SAFETY:``, ``// TODO:`` and other "tagged" comments, e.g.:
.. code-block:: rust
// FIXME: The error should be handled properly.
Comments should not be used for documentation purposes: comments are intended
for implementation details, not users. This distinction is useful even if the
reader of the source file is both an implementor and a user of an API. In fact,
sometimes it is useful to use both comments and documentation at the same time.
For instance, for a ``TODO`` list or to comment on the documentation itself.
For the latter case, comments can be inserted in the middle; that is, closer to
the line of documentation to be commented. For any other case, comments are
written after the documentation, e.g.:
.. code-block:: rust
/// Returns a new [`Foo`].
///
/// # Examples
///
// TODO: Find a better example.
/// ```
/// let foo = f(42);
/// ```
// FIXME: Use fallible approach.
pub fn f(x: i32) -> Foo {
// ...
}
This applies to both public and private items. This increases consistency with
public items, allows changes to visibility with less changes involved and will
allow us to potentially generate the documentation for private items as well.
In other words, if documentation is written for a private item, then ``///``
should still be used. For instance:
.. code-block:: rust
/// My private function.
// TODO: ...
fn f() {}
One special kind of comments are the ``// SAFETY:`` comments. These must appear
before every ``unsafe`` block, and they explain why the code inside the block is
correct/sound, i.e. why it cannot trigger undefined behavior in any case, e.g.:
.. code-block:: rust
// SAFETY: `p` is valid by the safety requirements.
unsafe { *p = 0; }
``// SAFETY:`` comments are not to be confused with the ``# Safety`` sections
in code documentation. ``# Safety`` sections specify the contract that callers
(for functions) or implementors (for traits) need to abide by. ``// SAFETY:``
comments show why a call (for functions) or implementation (for traits) actually
respects the preconditions stated in a ``# Safety`` section or the language
reference.
Code documentation
------------------
Rust kernel code is not documented like C kernel code (i.e. via kernel-doc).
Instead, the usual system for documenting Rust code is used: the ``rustdoc``
tool, which uses Markdown (a lightweight markup language).
To learn Markdown, there are many guides available out there. For instance,
the one at:
https://commonmark.org/help/
This is how a well-documented Rust function may look like:
.. code-block:: rust
/// Returns the contained [`Some`] value, consuming the `self` value,
/// without checking that the value is not [`None`].
///
/// # Safety
///
/// Calling this method on [`None`] is *[undefined behavior]*.
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
///
/// # Examples
///
/// ```
/// let x = Some("air");
/// assert_eq!(unsafe { x.unwrap_unchecked() }, "air");
/// ```
pub unsafe fn unwrap_unchecked(self) -> T {
match self {
Some(val) => val,
// SAFETY: The safety contract must be upheld by the caller.
None => unsafe { hint::unreachable_unchecked() },
}
}
This example showcases a few ``rustdoc`` features and some conventions followed
in the kernel:
- The first paragraph must be a single sentence briefly describing what
the documented item does. Further explanations must go in extra paragraphs.
- Unsafe functions must document their safety preconditions under
a ``# Safety`` section.
- While not shown here, if a function may panic, the conditions under which
that happens must be described under a ``# Panics`` section.
Please note that panicking should be very rare and used only with a good
reason. In almost all cases, a fallible approach should be used, typically
returning a ``Result``.
- If providing examples of usage would help readers, they must be written in
a section called ``# Examples``.
- Rust items (functions, types, constants...) must be linked appropriately
(``rustdoc`` will create a link automatically).
- Any ``unsafe`` block must be preceded by a ``// SAFETY:`` comment
describing why the code inside is sound.
While sometimes the reason might look trivial and therefore unneeded,
writing these comments is not just a good way of documenting what has been
taken into account, but most importantly, it provides a way to know that
there are no *extra* implicit constraints.
To learn more about how to write documentation for Rust and extra features,
please take a look at the ``rustdoc`` book at:
https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html
In addition, the kernel supports creating links relative to the source tree by
prefixing the link destination with ``srctree/``. For instance:
.. code-block:: rust
//! C header: [`include/linux/printk.h`](srctree/include/linux/printk.h)
or:
.. code-block:: rust
/// [`struct mutex`]: srctree/include/linux/mutex.h
C FFI types
-----------
Rust kernel code refers to C types, such as ``int``, using type aliases such as
``c_int``, which are readily available from the ``kernel`` prelude. Please do
not use the aliases from ``core::ffi`` -- they may not map to the correct types.
These aliases should generally be referred directly by their identifier, i.e.
as a single segment path. For instance:
.. code-block:: rust
fn f(p: *const c_char) -> c_int {
// ...
}
Naming
------
Rust kernel code follows the usual Rust naming conventions:
https://rust-lang.github.io/api-guidelines/naming.html
When existing C concepts (e.g. macros, functions, objects...) are wrapped into
a Rust abstraction, a name as close as reasonably possible to the C side should
be used in order to avoid confusion and to improve readability when switching
back and forth between the C and Rust sides. For instance, macros such as
``pr_info`` from C are named the same in the Rust side.
Having said that, casing should be adjusted to follow the Rust naming
conventions, and namespacing introduced by modules and types should not be
repeated in the item names. For instance, when wrapping constants like:
.. code-block:: c
#define GPIO_LINE_DIRECTION_IN 0
#define GPIO_LINE_DIRECTION_OUT 1
The equivalent in Rust may look like (ignoring documentation):
.. code-block:: rust
pub mod gpio {
pub enum LineDirection {
In = bindings::GPIO_LINE_DIRECTION_IN as _,
Out = bindings::GPIO_LINE_DIRECTION_OUT as _,
}
}
That is, the equivalent of ``GPIO_LINE_DIRECTION_IN`` would be referred to as
``gpio::LineDirection::In``. In particular, it should not be named
``gpio::gpio_line_direction::GPIO_LINE_DIRECTION_IN``.
Lints
-----
In Rust, it is possible to ``allow`` particular warnings (diagnostics, lints)
locally, making the compiler ignore instances of a given warning within a given
function, module, block, etc.
It is similar to ``#pragma GCC diagnostic push`` + ``ignored`` + ``pop`` in C
[#]_:
.. code-block:: c
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
static void f(void) {}
#pragma GCC diagnostic pop
.. [#] In this particular case, the kernel's ``__{always,maybe}_unused``
attributes (C23's ``[[maybe_unused]]``) may be used; however, the example
is meant to reflect the equivalent lint in Rust discussed afterwards.
But way less verbose:
.. code-block:: rust
#[allow(dead_code)]
fn f() {}
By that virtue, it makes it possible to comfortably enable more diagnostics by
default (i.e. outside ``W=`` levels). In particular, those that may have some
false positives but that are otherwise quite useful to keep enabled to catch
potential mistakes.
On top of that, Rust provides the ``expect`` attribute which takes this further.
It makes the compiler warn if the warning was not produced. For instance, the
following will ensure that, when ``f()`` is called somewhere, we will have to
remove the attribute:
.. code-block:: rust
#[expect(dead_code)]
fn f() {}
If we do not, we get a warning from the compiler::
warning: this lint expectation is unfulfilled
--> x.rs:3:10
|
3 | #[expect(dead_code)]
| ^^^^^^^^^
|
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
This means that ``expect``\ s do not get forgotten when they are not needed, which
may happen in several situations, e.g.:
- Temporary attributes added while developing.
- Improvements in lints in the compiler, Clippy or custom tools which may
remove a false positive.
- When the lint is not needed anymore because it was expected that it would be
removed at some point, such as the ``dead_code`` example above.
It also increases the visibility of the remaining ``allow``\ s and reduces the
chance of misapplying one.
Thus prefer ``expect`` over ``allow`` unless:
- Conditional compilation triggers the warning in some cases but not others.
If there are only a few cases where the warning triggers (or does not
trigger) compared to the total number of cases, then one may consider using
a conditional ``expect`` (i.e. ``cfg_attr(..., expect(...))``). Otherwise,
it is likely simpler to just use ``allow``.
- Inside macros, when the different invocations may create expanded code that
triggers the warning in some cases but not in others.
- When code may trigger a warning for some architectures but not others, such
as an ``as`` cast to a C FFI type.
As a more developed example, consider for instance this program:
.. code-block:: rust
fn g() {}
fn main() {
#[cfg(CONFIG_X)]
g();
}
Here, function ``g()`` is dead code if ``CONFIG_X`` is not set. Can we use
``expect`` here?
.. code-block:: rust
#[expect(dead_code)]
fn g() {}
fn main() {
#[cfg(CONFIG_X)]
g();
}
This would emit a lint if ``CONFIG_X`` is set, since it is not dead code in that
configuration. Therefore, in cases like this, we cannot use ``expect`` as-is.
A simple possibility is using ``allow``:
.. code-block:: rust
#[allow(dead_code)]
fn g() {}
fn main() {
#[cfg(CONFIG_X)]
g();
}
An alternative would be using a conditional ``expect``:
.. code-block:: rust
#[cfg_attr(not(CONFIG_X), expect(dead_code))]
fn g() {}
fn main() {
#[cfg(CONFIG_X)]
g();
}
This would ensure that, if someone introduces another call to ``g()`` somewhere
(e.g. unconditionally), then it would be spotted that it is not dead code
anymore. However, the ``cfg_attr`` is more complex than a simple ``allow``.
Therefore, it is likely that it is not worth using conditional ``expect``\ s when
more than one or two configurations are involved or when the lint may be
triggered due to non-local changes (such as ``dead_code``).
For more information about diagnostics in Rust, please see:
https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html
Error handling
--------------
For some background and guidelines about Rust for Linux specific error handling,
please see:
https://rust.docs.kernel.org/kernel/error/type.Result.html#error-codes-in-c-and-rust
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
스타일과 자동 서식
1-40이 문서는 커널 안에서 Rust 코드를 작성할 때 따라야 할 규칙을 설명한다. 코드는 `rustfmt`로 서식을 맞춘다. 가끔 커널에 기여하는 사람도 별도 스타일 가이드를 외울 필요가 없고, 검토자와 유지관리자가 서식 문제를 반복해서 지적하지 않아도 되어 패치 왕복을 줄일 수 있다.
주석과 문서화 규칙은 `rustfmt`가 검사하지 않으므로 작성자가 직접 지켜야 한다. 나머지는 기본 `rustfmt` 설정, 즉 관용적인 Rust 스타일을 따르며 들여쓰기는 탭 대신 공백 4개를 쓴다.
편집기나 IDE가 입력 중, 저장 시점 또는 커밋 시점에 자동 서식을 적용하도록 설정하면 편리하다. 전체 커널 Rust 소스를 다시 맞출 때는 `make LLVM=1 rustfmt`, CI처럼 변경 없이 서식만 검사하고 차이를 출력할 때는 `make LLVM=1 rustfmtcheck`를 사용한다.
`rustfmt`는 커널 C 코드의 `clang-format`처럼 개별 파일에 동작하며 커널 설정을 요구하지 않는다. 경우에 따라 아직 깨진 코드에도 적용할 수 있다.
작성 단계와 검증 단계가 같은 기본 rustfmt 규칙을 공유합니다.
.. SPDX-License-Identifier: GPL-2.0
Coding Guidelines
=================
This document describes how to write Rust code in the kernel.
Style & formatting
------------------
The code should be formatted using ``rustfmt``. In this way, a person
contributing from time to time to the kernel does not need to learn and
remember one more style guide. More importantly, reviewers and maintainers
do not need to spend time pointing out style issues anymore, and thus
less patch roundtrips may be needed to land a change.
.. note:: Conventions on comments and documentation are not checked by
``rustfmt``. Thus those are still needed to be taken care of.
The default settings of ``rustfmt`` are used. This means the idiomatic Rust
style is followed. For instance, 4 spaces are used for indentation rather
than tabs.
It is convenient to instruct editors/IDEs to format while typing,
when saving or at commit time. However, if for some reason reformatting
the entire kernel Rust sources is needed at some point, the following can be
run::
make LLVM=1 rustfmt
It is also possible to check if everything is formatted (printing a diff
otherwise), for instance for a CI, with::
make LLVM=1 rustfmtcheck
Like ``clang-format`` for the rest of the kernel, ``rustfmt`` works on
individual files, and does not require a kernel configuration. Sometimes it may
even work with broken code.
충돌을 줄이는 import 배치
41-116기본 `rustfmt`는 여러 import 항목을 같은 줄로 합칠 때가 있어 merge와 rebase 충돌을 일으키기 쉽다. 커널은 각 항목을 한 줄에 놓고 목록에 항목이 둘 이상이면 곧바로 중괄호를 쓰는 세로 배치를 사용한다.
목록 마지막에 붙이는 빈 주석 `//`는 이 배치를 보존한다. 기존의 압축된 import에 빈 주석을 추가한 뒤 `rustfmt`를 실행하면 중첩 항목까지 세로로 다시 정렬된다. 항목 하나뿐인 import에도 이 방법을 쓰면 패치 묶음 도중 목록이 늘고 줄어들 때 diff를 작게 유지할 수 있다.
빈 주석은 중괄호 안 어느 줄에나 둘 수 있지만 다른 formatter의 trailing comma와 비슷하게 보이도록 마지막 항목에 두는 편이 좋다. 패치 묶음에서 목록이 자주 변해 주석을 계속 옮겨야 한다면 더 단순한 위치를 선택할 수 있다.
이 규칙은 절대적인 금지는 아니어서 예외가 필요할 수 있고, 아직 이 형식으로 옮기지 않은 기존 코드도 있다. 그렇더라도 새 코드를 다른 형식으로 추가해서는 안 된다. 장기적으로는 안정판 `rustfmt`가 이와 비슷한 세로 형식을 직접 지원하게 하고 빈 주석을 제거하는 것이 목표다.
한 줄에 하나의 항목을 두어 변경 충돌을 국소화합니다.
Imports
~~~~~~~
``rustfmt``, by default, formats imports in a way that is prone to conflicts
while merging and rebasing, since in some cases it condenses several items into
the same line. For instance:
.. code-block:: rust
// Do not use this style.
use crate::{
example1,
example2::{example3, example4, example5},
example6, example7,
example8::example9,
};
Instead, the kernel uses a vertical layout that looks like this:
.. code-block:: rust
use crate::{
example1,
example2::{
example3,
example4,
example5, //
},
example6,
example7,
example8::example9, //
};
That is, each item goes into its own line, and braces are used as soon as there
is more than one item in a list.
The trailing empty comment allows to preserve this formatting. Not only that,
``rustfmt`` will actually reformat imports vertically when the empty comment is
added. That is, it is possible to easily reformat the original example into the
expected style by running ``rustfmt`` on an input like:
.. code-block:: rust
// Do not use this style.
use crate::{
example1,
example2::{example3, example4, example5, //
},
example6, example7,
example8::example9, //
};
The trailing empty comment works for nested imports, as shown above, as well as
for single item imports -- this can be useful to minimize diffs within patch
series:
.. code-block:: rust
use crate::{
example1, //
};
The trailing empty comment works in any of the lines within the braces, but it
is preferred to keep it in the last item, since it is reminiscent of the
trailing comma in other formatters. Sometimes it may be simpler to avoid moving
the comment several times within a patch series due to changes in the list.
There may be cases where exceptions may need to be made, i.e. none of this is
a hard rule. There is also code that is not migrated to this style yet, but
please do not introduce code in other styles.
Eventually, the goal is to get ``rustfmt`` to support this formatting style (or
a similar one) automatically in a stable release without requiring the trailing
empty comment. Thus, at some point, the goal is to remove those comments.
주석, 문서와 SAFETY 계약
117-191일반 주석 `//`도 렌더링되지는 않지만 문서 주석 `///`, `//!`과 같은 Markdown 방식으로 쓴다. 두 주석 종류의 규칙을 통일하고 내용을 서로 옮기기 쉽게 하기 위해서다. 문장은 대문자로 시작하고 마침표로 끝내며, 한 문장뿐이거나 `// SAFETY:`, `// TODO:`, `// FIXME:`처럼 꼬리표가 붙어도 같다.
일반 주석은 구현 세부사항을 위한 것이며 API 사용자에게 필요한 설명을 대신해서는 안 된다. 구현자와 사용자가 같은 소스 파일을 읽더라도 이 구분은 유용하다. 문서의 TODO 목록이나 문서 자체에 관한 설명처럼 둘을 함께 쓸 수 있으며, 특정 문서 줄을 설명하는 주석은 그 줄 가까이에 끼워 넣고 그 밖의 주석은 문서 주석 뒤에 둔다.
공개 항목과 비공개 항목 모두 같은 규칙을 적용한다. 비공개 항목에 문서를 쓴다면 일반 주석이 아니라 `///`를 사용한다. 그래야 공개 범위를 바꿀 때 수정이 적고, 나중에 비공개 항목 문서를 생성할 수도 있다.
모든 `unsafe` 블록 앞에는 `// SAFETY:` 주석을 두고 블록 안 코드가 어떤 경우에도 undefined behavior를 일으키지 않는 이유를 설명해야 한다. 이는 문서의 `# Safety` 절과 다르다. `# Safety`는 unsafe 함수의 호출자 또는 unsafe trait 구현자가 지켜야 할 계약이고, `// SAFETY:`는 실제 호출이나 구현이 그 선행조건과 언어 명세를 어떻게 만족하는지를 입증한다.
계약을 정의하는 문서와 계약 준수를 증명하는 주석을 구분합니다.
Comments
--------
"Normal" comments (i.e. ``//``, rather than code documentation which starts
with ``///`` or ``//!``) are written in Markdown the same way as documentation
comments are, even though they will not be rendered. This improves consistency,
simplifies the rules and allows to move content between the two kinds of
comments more easily. For instance:
.. code-block:: rust
// `object` is ready to be handled now.
f(object);
Furthermore, just like documentation, comments are capitalized at the beginning
of a sentence and ended with a period (even if it is a single sentence). This
includes ``// SAFETY:``, ``// TODO:`` and other "tagged" comments, e.g.:
.. code-block:: rust
// FIXME: The error should be handled properly.
Comments should not be used for documentation purposes: comments are intended
for implementation details, not users. This distinction is useful even if the
reader of the source file is both an implementor and a user of an API. In fact,
sometimes it is useful to use both comments and documentation at the same time.
For instance, for a ``TODO`` list or to comment on the documentation itself.
For the latter case, comments can be inserted in the middle; that is, closer to
the line of documentation to be commented. For any other case, comments are
written after the documentation, e.g.:
.. code-block:: rust
/// Returns a new [`Foo`].
///
/// # Examples
///
// TODO: Find a better example.
/// ```
/// let foo = f(42);
/// ```
// FIXME: Use fallible approach.
pub fn f(x: i32) -> Foo {
// ...
}
This applies to both public and private items. This increases consistency with
public items, allows changes to visibility with less changes involved and will
allow us to potentially generate the documentation for private items as well.
In other words, if documentation is written for a private item, then ``///``
should still be used. For instance:
.. code-block:: rust
/// My private function.
// TODO: ...
fn f() {}
One special kind of comments are the ``// SAFETY:`` comments. These must appear
before every ``unsafe`` block, and they explain why the code inside the block is
correct/sound, i.e. why it cannot trigger undefined behavior in any case, e.g.:
.. code-block:: rust
// SAFETY: `p` is valid by the safety requirements.
unsafe { *p = 0; }
``// SAFETY:`` comments are not to be confused with the ``# Safety`` sections
in code documentation. ``# Safety`` sections specify the contract that callers
(for functions) or implementors (for traits) need to abide by. ``// SAFETY:``
comments show why a call (for functions) or implementation (for traits) actually
respects the preconditions stated in a ``# Safety`` section or the language
reference.
rustdoc 문서 작성 규칙
192-280커널 Rust 코드는 C의 kernel-doc이 아니라 Rust의 표준 문서화 도구 `rustdoc`과 Markdown을 사용한다. Markdown 입문에는 `https://commonmark.org/help/`를 참고할 수 있다.
잘 작성된 함수 문서의 첫 문단은 항목이 무엇을 하는지 짧게 설명하는 한 문장이어야 하고, 추가 설명은 다음 문단으로 분리한다. Unsafe 함수는 `# Safety` 절에 안전 선행조건을 기록한다. 함수가 panic할 수 있다면 `# Panics` 절에 조건을 적지만, 커널에서 panic은 좋은 이유가 있을 때만 매우 드물게 사용하고 대개 `Result`를 반환하는 실패 가능 방식으로 설계한다.
사용 예제가 독자에게 도움이 되면 `# Examples` 절에 둔다. 함수, 타입, 상수 같은 Rust 항목은 `rustdoc`이 자동 링크를 만들 수 있도록 알맞게 연결한다. 모든 unsafe 블록 앞의 `// SAFETY:`는 단순해 보이는 이유까지 기록함으로써 검토한 조건과 숨은 추가 제약이 없음을 함께 보여 준다.
더 자세한 문서 기능은 `https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html`에서 볼 수 있다. 커널은 링크 목적지 앞에 `srctree/`를 붙여 소스 트리 상대 링크를 만들 수 있다. 예를 들어 `include/linux/printk.h` 또는 `include/linux/mutex.h`의 `struct mutex`로 직접 연결할 수 있다.
독자가 API의 동작, 위험과 사용법을 빠르게 찾도록 구조를 고정합니다.
Code documentation
------------------
Rust kernel code is not documented like C kernel code (i.e. via kernel-doc).
Instead, the usual system for documenting Rust code is used: the ``rustdoc``
tool, which uses Markdown (a lightweight markup language).
To learn Markdown, there are many guides available out there. For instance,
the one at:
https://commonmark.org/help/
This is how a well-documented Rust function may look like:
.. code-block:: rust
/// Returns the contained [`Some`] value, consuming the `self` value,
/// without checking that the value is not [`None`].
///
/// # Safety
///
/// Calling this method on [`None`] is *[undefined behavior]*.
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
///
/// # Examples
///
/// ```
/// let x = Some("air");
/// assert_eq!(unsafe { x.unwrap_unchecked() }, "air");
/// ```
pub unsafe fn unwrap_unchecked(self) -> T {
match self {
Some(val) => val,
// SAFETY: The safety contract must be upheld by the caller.
None => unsafe { hint::unreachable_unchecked() },
}
}
This example showcases a few ``rustdoc`` features and some conventions followed
in the kernel:
- The first paragraph must be a single sentence briefly describing what
the documented item does. Further explanations must go in extra paragraphs.
- Unsafe functions must document their safety preconditions under
a ``# Safety`` section.
- While not shown here, if a function may panic, the conditions under which
that happens must be described under a ``# Panics`` section.
Please note that panicking should be very rare and used only with a good
reason. In almost all cases, a fallible approach should be used, typically
returning a ``Result``.
- If providing examples of usage would help readers, they must be written in
a section called ``# Examples``.
- Rust items (functions, types, constants...) must be linked appropriately
(``rustdoc`` will create a link automatically).
- Any ``unsafe`` block must be preceded by a ``// SAFETY:`` comment
describing why the code inside is sound.
While sometimes the reason might look trivial and therefore unneeded,
writing these comments is not just a good way of documenting what has been
taken into account, but most importantly, it provides a way to know that
there are no *extra* implicit constraints.
To learn more about how to write documentation for Rust and extra features,
please take a look at the ``rustdoc`` book at:
https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html
In addition, the kernel supports creating links relative to the source tree by
prefixing the link destination with ``srctree/``. For instance:
.. code-block:: rust
//! C header: [`include/linux/printk.h`](srctree/include/linux/printk.h)
or:
.. code-block:: rust
/// [`struct mutex`]: srctree/include/linux/mutex.h
C FFI 타입 표기
281-297커널 Rust 코드에서 C의 `int` 같은 타입을 가리킬 때는 `kernel` prelude가 제공하는 `c_int`, `c_char` 같은 별칭을 사용한다. `core::ffi`의 별칭은 커널 C 쪽의 실제 타입과 올바르게 대응하지 않을 수 있으므로 사용하지 않는다.
이 별칭은 보통 모듈 경로를 붙이지 않은 단일 식별자로 쓴다. 따라서 C 문자열 포인터를 받고 C 정수를 반환하는 함수는 `fn f(p: *const c_char) -> c_int`처럼 적는다.
커널 prelude가 C ABI와 맞는 타입 별칭을 제공합니다.
C FFI types
-----------
Rust kernel code refers to C types, such as ``int``, using type aliases such as
``c_int``, which are readily available from the ``kernel`` prelude. Please do
not use the aliases from ``core::ffi`` -- they may not map to the correct types.
These aliases should generally be referred directly by their identifier, i.e.
as a single segment path. For instance:
.. code-block:: rust
fn f(p: *const c_char) -> c_int {
// ...
}
C 개념을 Rust 이름으로 옮기기
298-335커널 Rust 코드는 일반 Rust API naming guideline을 따른다. 기준은 `https://rust-lang.github.io/api-guidelines/naming.html`이다.
기존 C macro, 함수, 객체를 Rust 추상화로 감쌀 때는 C와 Rust 코드를 오가며 읽기 쉽도록 합리적인 범위에서 C 이름과 가깝게 짓는다. 예를 들어 C의 `pr_info` macro는 Rust에서도 같은 이름을 쓴다.
다만 대소문자 형식은 Rust 관례로 바꾸고, 모듈과 타입이 이미 제공하는 namespace를 항목 이름에 되풀이하지 않는다. C의 `GPIO_LINE_DIRECTION_IN`과 `GPIO_LINE_DIRECTION_OUT`은 Rust에서 `gpio::LineDirection::In`, `gpio::LineDirection::Out`이 된다. `gpio::gpio_line_direction::GPIO_LINE_DIRECTION_IN`처럼 접두어를 중복해서는 안 된다.
원래 개념은 알아볼 수 있게 두고 Rust namespace와 casing을 적용합니다.
Naming
------
Rust kernel code follows the usual Rust naming conventions:
https://rust-lang.github.io/api-guidelines/naming.html
When existing C concepts (e.g. macros, functions, objects...) are wrapped into
a Rust abstraction, a name as close as reasonably possible to the C side should
be used in order to avoid confusion and to improve readability when switching
back and forth between the C and Rust sides. For instance, macros such as
``pr_info`` from C are named the same in the Rust side.
Having said that, casing should be adjusted to follow the Rust naming
conventions, and namespacing introduced by modules and types should not be
repeated in the item names. For instance, when wrapping constants like:
.. code-block:: c
#define GPIO_LINE_DIRECTION_IN 0
#define GPIO_LINE_DIRECTION_OUT 1
The equivalent in Rust may look like (ignoring documentation):
.. code-block:: rust
pub mod gpio {
pub enum LineDirection {
In = bindings::GPIO_LINE_DIRECTION_IN as _,
Out = bindings::GPIO_LINE_DIRECTION_OUT as _,
}
}
That is, the equivalent of ``GPIO_LINE_DIRECTION_IN`` would be referred to as
``gpio::LineDirection::In``. In particular, it should not be named
``gpio::gpio_line_direction::GPIO_LINE_DIRECTION_IN``.
allow보다 expect를 우선하는 lint 정책
336-480Rust는 함수, 모듈, 블록 같은 국소 범위에서 특정 warning, diagnostic 또는 lint를 `allow`할 수 있다. C의 `#pragma GCC diagnostic push`, `ignored`, `pop` 조합과 비슷하지만 `#[allow(dead_code)]`처럼 훨씬 간결하다. 이 기능 덕분에 false positive 가능성이 조금 있어도 실수를 잘 잡는 진단을 기본 `W=` 단계 밖에서 더 많이 켤 수 있다.
`expect` 속성은 지정한 warning이 실제로 발생하지 않으면 컴파일러가 다시 경고한다. 따라서 `#[expect(dead_code)]`를 붙인 함수가 사용되기 시작하면 `unfulfilled_lint_expectations`가 속성을 제거하라고 알려 준다. 개발 중 임시 속성, compiler·Clippy·사용자 도구의 lint 개선으로 사라진 false positive, 언젠가 없어질 것으로 예상한 dead code 억제가 잊히지 않는다.
그래서 원칙적으로 `allow`보다 `expect`를 선호한다. 다만 조건부 컴파일에 따라 경고가 생겼다 사라지는 경우, macro 호출마다 펼쳐진 코드의 경고 여부가 다른 경우, C FFI 타입으로의 `as` cast처럼 아키텍처에 따라 경고가 달라지는 경우에는 `allow`가 더 알맞을 수 있다.
조건부 컴파일 사례에서 `g()`가 `CONFIG_X`일 때만 호출되면 무조건적인 `#[expect(dead_code)]`는 `CONFIG_X=y` 구성에서 기대가 충족되지 않았다는 lint를 낸다. 단순하게 `#[allow(dead_code)]`를 쓰거나, `#[cfg_attr(not(CONFIG_X), expect(dead_code))]`로 경고가 생기는 구성에서만 기대하도록 만들 수 있다.
조건부 `expect`는 다른 무조건 호출이 추가되어 dead code가 아니게 된 변화를 발견한다는 장점이 있지만 단순한 `allow`보다 복잡하다. 구성 경우가 한두 개를 넘거나 `dead_code`처럼 비국소 변경으로 lint 발생 여부가 바뀌면 그 복잡성이 대개 이득보다 크다. 상세 진단 규칙은 Rust reference의 diagnostics attributes 문서를 참고한다.
억제 사유가 사라졌을 때 자동으로 드러나는 expect를 기본으로 합니다.
필요한 동안만 억제를 유지하고 사유가 사라지면 compiler가 알려 줍니다.
Lints
-----
In Rust, it is possible to ``allow`` particular warnings (diagnostics, lints)
locally, making the compiler ignore instances of a given warning within a given
function, module, block, etc.
It is similar to ``#pragma GCC diagnostic push`` + ``ignored`` + ``pop`` in C
[#]_:
.. code-block:: c
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
static void f(void) {}
#pragma GCC diagnostic pop
.. [#] In this particular case, the kernel's ``__{always,maybe}_unused``
attributes (C23's ``[[maybe_unused]]``) may be used; however, the example
is meant to reflect the equivalent lint in Rust discussed afterwards.
But way less verbose:
.. code-block:: rust
#[allow(dead_code)]
fn f() {}
By that virtue, it makes it possible to comfortably enable more diagnostics by
default (i.e. outside ``W=`` levels). In particular, those that may have some
false positives but that are otherwise quite useful to keep enabled to catch
potential mistakes.
On top of that, Rust provides the ``expect`` attribute which takes this further.
It makes the compiler warn if the warning was not produced. For instance, the
following will ensure that, when ``f()`` is called somewhere, we will have to
remove the attribute:
.. code-block:: rust
#[expect(dead_code)]
fn f() {}
If we do not, we get a warning from the compiler::
warning: this lint expectation is unfulfilled
--> x.rs:3:10
|
3 | #[expect(dead_code)]
| ^^^^^^^^^
|
= note: `#[warn(unfulfilled_lint_expectations)]` on by default
This means that ``expect``\ s do not get forgotten when they are not needed, which
may happen in several situations, e.g.:
- Temporary attributes added while developing.
- Improvements in lints in the compiler, Clippy or custom tools which may
remove a false positive.
- When the lint is not needed anymore because it was expected that it would be
removed at some point, such as the ``dead_code`` example above.
It also increases the visibility of the remaining ``allow``\ s and reduces the
chance of misapplying one.
Thus prefer ``expect`` over ``allow`` unless:
- Conditional compilation triggers the warning in some cases but not others.
If there are only a few cases where the warning triggers (or does not
trigger) compared to the total number of cases, then one may consider using
a conditional ``expect`` (i.e. ``cfg_attr(..., expect(...))``). Otherwise,
it is likely simpler to just use ``allow``.
- Inside macros, when the different invocations may create expanded code that
triggers the warning in some cases but not in others.
- When code may trigger a warning for some architectures but not others, such
as an ``as`` cast to a C FFI type.
As a more developed example, consider for instance this program:
.. code-block:: rust
fn g() {}
fn main() {
#[cfg(CONFIG_X)]
g();
}
Here, function ``g()`` is dead code if ``CONFIG_X`` is not set. Can we use
``expect`` here?
.. code-block:: rust
#[expect(dead_code)]
fn g() {}
fn main() {
#[cfg(CONFIG_X)]
g();
}
This would emit a lint if ``CONFIG_X`` is set, since it is not dead code in that
configuration. Therefore, in cases like this, we cannot use ``expect`` as-is.
A simple possibility is using ``allow``:
.. code-block:: rust
#[allow(dead_code)]
fn g() {}
fn main() {
#[cfg(CONFIG_X)]
g();
}
An alternative would be using a conditional ``expect``:
.. code-block:: rust
#[cfg_attr(not(CONFIG_X), expect(dead_code))]
fn g() {}
fn main() {
#[cfg(CONFIG_X)]
g();
}
This would ensure that, if someone introduces another call to ``g()`` somewhere
(e.g. unconditionally), then it would be spotted that it is not dead code
anymore. However, the ``cfg_attr`` is more complex than a simple ``allow``.
Therefore, it is likely that it is not worth using conditional ``expect``\ s when
more than one or two configurations are involved or when the lint may be
triggered due to non-local changes (such as ``dead_code``).
For more information about diagnostics in Rust, please see:
https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html
Rust for Linux 오류 처리
481-487Rust for Linux에 특화된 오류 처리의 배경과 지침은 `https://rust.docs.kernel.org/kernel/error/type.Result.html#error-codes-in-c-and-rust`를 참고한다. C의 오류 코드와 Rust의 `Result`를 연결할 때 적용할 커널 규칙을 이 문서가 설명한다.
C 오류 코드를 관용적인 Rust 실패 경로로 바꿉니다.
Error handling
--------------
For some background and guidelines about Rust for Linux specific error handling,
please see:
https://rust.docs.kernel.org/kernel/error/type.Result.html#error-codes-in-c-and-rust
요약·해설
coding-guidelines.rst:1-487rustfmt, import, 주석과 rustdoc, C FFI 이름, lint와 오류 처리 규칙을 안전성 계약 중심으로 설명합니다.