Coding Guidelines

This document describes how to write Rust code in the kernel.

Style & formatting

The code should be formatted usingrustfmt. In this way, a personcontributing from time to time to the kernel does not need to learn andremember one more style guide. More importantly, reviewers and maintainersdo not need to spend time pointing out style issues anymore, and thusless 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 ofrustfmt are used. This means the idiomatic Ruststyle is followed. For instance, 4 spaces are used for indentation ratherthan tabs.

It is convenient to instruct editors/IDEs to format while typing,when saving or at commit time. However, if for some reason reformattingthe entire kernel Rust sources is needed at some point, the following can berun:

make LLVM=1 rustfmt

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

make LLVM=1 rustfmtcheck

Likeclang-format for the rest of the kernel,rustfmt works onindividual files, and does not require a kernel configuration. Sometimes it mayeven work with broken code.

Imports

rustfmt, by default, formats imports in a way that is prone to conflictswhile merging and rebasing, since in some cases it condenses several items intothe same line. For instance:

// Do not use this style.usecrate::{example1,example2::{example3,example4,example5},example6,example7,example8::example9,};

Instead, the kernel uses a vertical layout that looks like this:

usecrate::{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 thereis 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 isadded. That is, it is possible to easily reformat the original example into theexpected style by runningrustfmt on an input like:

// Do not use this style.usecrate::{example1,example2::{example3,example4,example5,//},example6,example7,example8::example9,//};

The trailing empty comment works for nested imports, as shown above, as well asfor single item imports -- this can be useful to minimize diffs within patchseries:

usecrate::{example1,//};

The trailing empty comment works in any of the lines within the braces, but itis preferred to keep it in the last item, since it is reminiscent of thetrailing comma in other formatters. Sometimes it may be simpler to avoid movingthe 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 isa hard rule. There is also code that is not migrated to this style yet, butplease do not introduce code in other styles.

Eventually, the goal is to getrustfmt to support this formatting style (ora similar one) automatically in a stable release without requiring the trailingempty comment. Thus, at some point, the goal is to remove those comments.

Comments

“Normal” comments (i.e.//, rather than code documentation which startswith/// or//!) are written in Markdown the same way as documentationcomments are, even though they will not be rendered. This improves consistency,simplifies the rules and allows to move content between the two kinds ofcomments more easily. For instance:

// `object` is ready to be handled now.f(object);

Furthermore, just like documentation, comments are capitalized at the beginningof a sentence and ended with a period (even if it is a single sentence). Thisincludes//SAFETY:,//TODO: and other “tagged” comments, e.g.:

// FIXME: The error should be handled properly.

Comments should not be used for documentation purposes: comments are intendedfor implementation details, not users. This distinction is useful even if thereader 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 aTODO list or to comment on the documentation itself.For the latter case, comments can be inserted in the middle; that is, closer tothe line of documentation to be commented. For any other case, comments arewritten after the documentation, e.g.:

/// Returns a new [`Foo`].////// # Examples///// TODO: Find a better example./// ```/// let foo = f(42);/// ```// FIXME: Use fallible approach.pubfnf(x:i32)->Foo{// ...}

This applies to both public and private items. This increases consistency withpublic items, allows changes to visibility with less changes involved and willallow 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:

/// My private function.// TODO: ...fnf(){}

One special kind of comments are the//SAFETY: comments. These must appearbefore everyunsafe block, and they explain why the code inside the block iscorrect/sound, i.e. why it cannot trigger undefined behavior in any case, e.g.:

// SAFETY: `p` is valid by the safety requirements.unsafe{*p=0;}

//SAFETY: comments are not to be confused with the#Safety sectionsin 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) actuallyrespects the preconditions stated in a#Safety section or the languagereference.

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: therustdoctool, 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");/// ```pubunsafefnunwrap_unchecked(self)->T{matchself{Some(val)=>val,// SAFETY: The safety contract must be upheld by the caller.None=>unsafe{hint::unreachable_unchecked()},}}

This example showcases a fewrustdoc features and some conventions followedin the kernel:

  • The first paragraph must be a single sentence briefly describing whatthe documented item does. Further explanations must go in extra paragraphs.

  • Unsafe functions must document their safety preconditions undera#Safety section.

  • While not shown here, if a function may panic, the conditions under whichthat happens must be described under a#Panics section.

    Please note that panicking should be very rare and used only with a goodreason. In almost all cases, a fallible approach should be used, typicallyreturning aResult.

  • If providing examples of usage would help readers, they must be written ina section called#Examples.

  • Rust items (functions, types, constants...) must be linked appropriately(rustdoc will create a link automatically).

  • Anyunsafe block must be preceded by a//SAFETY: commentdescribing 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 beentaken into account, but most importantly, it provides a way to know thatthere are noextra implicit constraints.

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

In addition, the kernel supports creating links relative to the source tree byprefixing the link destination withsrctree/. For instance:

//! C header: [`include/linux/printk.h`](srctree/include/linux/printk.h)

or:

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

C FFI types

Rust kernel code refers to C types, such asint, using type aliases such asc_int, which are readily available from thekernel prelude. Please donot use the aliases fromcore::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:

fnf(p:*constc_char)->c_int{// ...}

Naming

Rust kernel code follows the usual Rust naming conventions:

When existing C concepts (e.g. macros, functions, objects...) are wrapped intoa Rust abstraction, a name as close as reasonably possible to the C side shouldbe used in order to avoid confusion and to improve readability when switchingback 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 namingconventions, and namespacing introduced by modules and types should not berepeated 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):

pubmodgpio{pubenumLineDirection{In=bindings::GPIO_LINE_DIRECTION_INas_,Out=bindings::GPIO_LINE_DIRECTION_OUTas_,}}

That is, the equivalent ofGPIO_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 toallow particular warnings (diagnostics, lints)locally, making the compiler ignore instances of a given warning within a givenfunction, module, block, etc.

It is similar to#pragmaGCCdiagnosticpush +ignored +pop in C[1]:

#pragma GCC diagnostic push#pragma GCC diagnostic ignored "-Wunused-function"staticvoidf(void){}#pragma GCC diagnostic pop
[1]

In this particular case, the kernel’s__{always,maybe}_unusedattributes (C23’s[[maybe_unused]]) may be used; however, the exampleis meant to reflect the equivalent lint in Rust discussed afterwards.

But way less verbose:

#[allow(dead_code)]fnf(){}

By that virtue, it makes it possible to comfortably enable more diagnostics bydefault (i.e. outsideW= levels). In particular, those that may have somefalse positives but that are otherwise quite useful to keep enabled to catchpotential mistakes.

On top of that, Rust provides theexpect attribute which takes this further.It makes the compiler warn if the warning was not produced. For instance, thefollowing will ensure that, whenf() is called somewhere, we will have toremove the attribute:

#[expect(dead_code)]fnf(){}

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 thatexpects do not get forgotten when they are not needed, whichmay happen in several situations, e.g.:

  • Temporary attributes added while developing.

  • Improvements in lints in the compiler, Clippy or custom tools which mayremove a false positive.

  • When the lint is not needed anymore because it was expected that it would beremoved at some point, such as thedead_code example above.

It also increases the visibility of the remainingallows and reduces thechance of misapplying one.

Thus preferexpect overallow 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 nottrigger) compared to the total number of cases, then one may consider usinga conditionalexpect (i.e.cfg_attr(...,expect(...))). Otherwise,it is likely simpler to just useallow.

  • Inside macros, when the different invocations may create expanded code thattriggers the warning in some cases but not in others.

  • When code may trigger a warning for some architectures but not others, suchas anas cast to a C FFI type.

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

fng(){}fnmain(){#[cfg(CONFIG_X)]g();}

Here, functiong() is dead code ifCONFIG_X is not set. Can we useexpect here?

#[expect(dead_code)]fng(){}fnmain(){#[cfg(CONFIG_X)]g();}

This would emit a lint ifCONFIG_X is set, since it is not dead code in thatconfiguration. Therefore, in cases like this, we cannot useexpect as-is.

A simple possibility is usingallow:

#[allow(dead_code)]fng(){}fnmain(){#[cfg(CONFIG_X)]g();}

An alternative would be using a conditionalexpect:

#[cfg_attr(not(CONFIG_X), expect(dead_code))]fng(){}fnmain(){#[cfg(CONFIG_X)]g();}

This would ensure that, if someone introduces another call tog() somewhere(e.g. unconditionally), then it would be spotted that it is not dead codeanymore. However, thecfg_attr is more complex than a simpleallow.

Therefore, it is likely that it is not worth using conditionalexpects whenmore than one or two configurations are involved or when the lint may betriggered due to non-local changes (such asdead_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: