mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-28 16:56:26 +00:00
b160dc46dd
This list started as a "when to prefer `expect`" list, but at some point
during writing I changed it to a "prefer `expect` unless..." one. However,
the first bullet remained, which does not make sense anymore.
Thus remove it. In addition, fix nearby typo.
Fixes: 04866494e9
("Documentation: rust: discuss `#[expect(...)]` in the guidelines")
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Link: https://lore.kernel.org/r/20241117133127.473937-1-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
376 lines
12 KiB
ReStructuredText
376 lines
12 KiB
ReStructuredText
.. 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.
|
|
|
|
|
|
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 {
|
|
// ...
|
|
}
|
|
|
|
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
|
|
|
|
|
|
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
|