Skip to content

Re-order imports #12723

@overlookmotel

Description

@overlookmotel

We (mostly) have a convention that we order imports in Rust code in order of "furthest away" to "closest".

The order

The full order is:

  1. std
  2. External crates
  3. Oxc crates
  4. Local crate (crate)
  5. super
  6. mod

e.g.:

use std::{env, path::Path, sync::Arc};

use itertools::Itertools;
use rustc_hash::FxHashMap;

use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_semantic::{SemanticBuilder, dot::DebugDot};
use oxc_span::SourceType;

use crate::{Foo, Bar};

use super::{Qux, blah_blah::{example, words}};

mod something;
mod something_else;
pub use something::Thing;
use something_else::{AnotherThing, Stuff};

Within each category, order should be alphabetical. rustfmt will enforce that.

"Oxc crates" means any crate whose name begins with oxc_. A few of those crates are outside of this monorepo e.g. oxc_index, but that is not relevant. They are still considered "Oxc crates".

Source priority

Imports should be imported from the closest available source.

  • Prefer to use from a local mod over super or crate.
  • Prefer to use from super over crate.

Example of good practice

// lib.rs
mod foo;
use foo::bar::Stuff;
// foo/mod.rs
pub mod bar;
use bar::Stuff;
// foo/bar.rs
pub struct Stuff {}

Example of bad practice

// foo/mod.rs
use crate::Stuff;

pub mod stuff;

Position of imports vs other code

All imports must be before all other code in the file. The only exceptions are:

  1. Module-level doc comments - //! ...
  2. Module-level attributes - e.g. #![expect(clippy::print_stdout)]

Module-level doc comments should be at the very top of the file. Module-level attributes come next.

//! This module relates to blah.

#![expect(clippy::print_stdout)]

use std::mem;

pub struct Foo {
    yeah: bool,
}

Flexibility of the rules

The rules governing order are inflexible, except for 2 exceptions:

Feature gates

Sometimes it's clearer to break up feature-gated imports into a separate block.

use abc::def;
use xyz::zed;

#[cfg(feature = "serialize")]
use serde::Serialize;

Note that serde is before xyz alphabetically, but appears here after xyz. That's OK, because we want to separate out the feature-gated import from the rest.

mod category

There can be some flexibility in the last category (mod statements, and use statements importing from those modules).

Sometimes it's better for readability to break up a lot of mod statements into multiple blocks, separate out pub use from use statements, or put line breaks between groups of statements that fit in these categories. This may make the code easier to read in some circumstances.

But the hard-and-fast rules concerning the last category are:

  • mod must always be before any use statements relating to that mod.
  • mod must always be after all other categories (after use crate::..., use super::... etc).

Line breaks

Note that there needs to be a line break in between the different categories of imports, otherwise rustfmt will re-order statements automatically, in a way which violates the above rules.

Additionally, any module-level attributes should have a line break between them and the first use statement:

#![expect(clippy::print_stdout)]

use std::mem;

Ditto any module doc comments should have a line break after it before the first use:

//! This module relates to stuff.

use std::mem;

Module-level doc comments and attributes should also be separated from each other by line breaks.

//! This module relates to stuff.

#![expect(clippy::print_stdout)]

The last use / mod statement should have a line break after it, before any other code ("code" here includes comments or #[...] attributes).

use crate::Yeah;

/// Amazing data structure
#[derive(Debug)]
pub struct Amazing {
    hell: Yeah,
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-cleanupCategory - technical debt or refactoring. Solution not expected to change behavior

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions