Coding Guidelines — The Linux Kernel documentation (original) (raw)

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 byrustfmt. 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:

It is also possible to check if everything is formatted (printing a diff otherwise), for instance for a CI, with:

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.

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 rustdoctool, which uses Markdown (a lightweight markup language).

To learn Markdown, there are many guides available out there. For instance, the one at:

This is how a well-documented Rust function may look like:

/// 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:

To learn more about how to write documentation for Rust and extra features, please take a look at the rustdoc book at:

In addition, the kernel supports creating links relative to the source tree by prefixing the link destination with srctree/. For instance:

//! C header: include/linux/printk.h

or:

/// [struct mutex]: srctree/include/linux/mutex.h

Naming

Rust kernel code follows the usual Rust naming conventions:

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 aspr_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:

#define GPIO_LINE_DIRECTION_IN 0 #define GPIO_LINE_DIRECTION_OUT 1

The equivalent in Rust may look like (ignoring documentation):

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 asgpio::LineDirection::In. In particular, it should not be namedgpio::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[1]:

#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" static void f(void) {} #pragma GCC diagnostic pop

But way less verbose:

#[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:

#[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 expects do not get forgotten when they are not needed, which may happen in several situations, e.g.:

It also increases the visibility of the remaining allows and reduces the chance of misapplying one.

Thus prefer expect over allow unless:

As a more developed example, consider for instance this program:

fn g() {}

fn main() { #[cfg(CONFIG_X)] g(); }

Here, function g() is dead code if CONFIG_X is not set. Can we useexpect here?

#[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:

#[allow(dead_code)] fn g() {}

fn main() { #[cfg(CONFIG_X)] g(); }

An alternative would be using a conditional expect:

#[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 expects 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:

Error handling

For some background and guidelines about Rust for Linux specific error handling, please see: