Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

格式

使用 rustfmt 进行自动格式化代码

应该总是在项目中添加 rustfmt.toml .rustfmt.toml文件。即使它是空文件,这是向潜在的合作者表明你希望代码是自动格式化的。

在特殊的情况下,可以通过条件编译属性 #[rustfmt::skip] 来关闭自动格式化。

fn main() {
    #[rustfmt::skip] 
    let got = vec![
            0x00, 0x05, 0x01, 0x00,
            0xff,
            0x00,
            0x00,
    
            0x01, 0x0c, 0x02, 0x00,
            0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
            b'd', b'e', b'a', b'd', b'b', b'e', b'e', b'f', 0x00,
            0x00,
    
            127, 0x06, 0x03, 0x00,
            0x01, 0x02,
            b'a', b'b', b'c', b'd', 0x00,
            b'1', b'2', b'3', b'4', 0x00,
            0x00,
    ];
}

行间距最大宽度空一行

代码行之间,最小间隔 0 行,最大间隔1行。

存在多个标识符时应该保持块状(Block)缩进

当在表达式或语言项定义中出现多个标识符,则应该让其保持块状风格缩进。

数组:

fn main() {
    // 符合: 缩进四个空格
    let lorem = vec![
        "ipsum",
        "dolor",
        "sit",
        "amet",
        "consectetur",
        "adipiscing",
        "elit",
    ];
}

流程控制:

fn main() {
    // 符合: 缩进四个空格
    if lorem_ipsum
        && dolor_sit // 注意:没有和前一行 `lorem_ipsum`对齐
        && amet_consectetur
        && lorem_sit
        && dolor_consectetur
        && amet_ipsum
        && lorem_consectetur
    {
        // ...
    }
}

函数参数:

fn main() {
    fn lorem() {}
    fn lorem(ipsum: usize) {}
    // 符合: 缩进四个空格
    fn lorem(
        ipsum: usize,
        dolor: usize,
        sit: usize,
        amet: usize,
        consectetur: usize,
        adipiscing: usize,
        elit: usize,
    ) {
        // body
    }
}

函数调用:

fn main() {
    // 符合: 缩进四个空格
    lorem(
        "lorem",
        "ipsum",
        "dolor",
        "sit",
        "amet",
        "consectetur",
        "adipiscing",
        "elit",
    );
}

泛型:

#![allow(unused)]
fn main() {
// 符合: 缩进四个空格
fn lorem<
    Ipsum,
    Dolor,
    Sit,
    Amet,
    Adipiscing,
    Consectetur,
    Elit,
>(
    ipsum: Ipsum,
    dolor: Dolor,
    sit: Sit,
    amet: Amet,
    adipiscing: Adipiscing,
    consectetur: Consectetur,
    elit: Elit,
) -> T {
    // body
}
}

结构体:

fn main() {
    let lorem = Lorem {
        ipsum: dolor,
        sit: amet,
    };
}

当有多行表达式操作时,操作符应该置于行首

当有多行表达式操作时,操作符应该置于行首,这样有利于代码的可读性和可维护性。

fn main() {
    // 不符合
    let or = foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo ||
        barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar;
    // 不符合
    let sum = 123456789012345678901234567890 +
        123456789012345678901234567890 +
        123456789012345678901234567890;
    // 不符合
    let range = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..
        bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
    
    // 符合
    let or = foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
        || barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar;

    // 符合
    let sum = 123456789012345678901234567890
        + 123456789012345678901234567890
        + 123456789012345678901234567890;
    // 符合
    let range = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
        ..bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
}

枚举变体和结构体字段都应左对齐

对于自定义了判别式的枚举体,和有字段的结构体而言,默认只需要左对齐就可以。


#![allow(unused)]
fn main() {
// 符合: 无论变体长度多长,都左对齐
enum Bar {
    A = 0,
    Bb = 1,
    RandomLongVariantGoesHere = 10,
    Ccc = 71,
}
// 符合
enum Bar {
    VeryLongVariantNameHereA = 0,
    VeryLongVariantNameHereBb = 1,
    VeryLongVariantNameHereCcc = 2,
}
}

函数参数超过五个或导入模块个数超过四个需换行

  • 五个以内函数参数可以置于一行,超过五个则使用「块」状缩进
  • 导入模块每行超过四个,则换行

fn main() {
trait Lorem {
    fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur);

    fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
        // body
    }

    // 符合
    fn lorem(
        ipsum: Ipsum,
        dolor: Dolor,
        sit: Sit,
        amet: Amet,
        consectetur: Consectetur,
        adipiscing: Adipiscing,
        elit: Elit,
    );

    // 符合
    fn lorem(
        ipsum: Ipsum,
        dolor: Dolor,
        sit: Sit,
        amet: Amet,
        consectetur: Consectetur,
        adipiscing: Adipiscing,
        elit: Elit,
    ) {
        // body
    }
}

use foo::{xxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyyyyyyy, zzzzzzzzzzzzzzzzzz};

// 符合
use foo::{
    aaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb, cccccccccccccccccc, dddddddddddddddddd,
    eeeeeeeeeeeeeeeeee,
};
}

不同的场景,使用不同的空格风格

  • 在冒号之后添加空格,在冒号之前不要加空格
  • 在范围(range)操作符(..和..=)前后不要使用空格
  • 在+或=操作符前后要加空格
fn main() {
// 符合
fn lorem<T: Eq>(t: T) {
    let lorem: Dolor = Lorem {
        ipsum: dolor,
        sit: amet,
    };
}

// 符合
fn lorem<T: Eq>(t: T) {
    let lorem: Dolor = Lorem {
        ipsum: dolor,
        sit: amet,
    };
}

// 符合
let lorem = 0..10;
let ipsum = 0..=10;
}

match 分支应该具有良好的可读性

  • 当match分支右侧代码体太长无法和=>置于同一行需要使用块(block)来包裹
  • 在match分支左侧匹配表达式前不要增加管道符(|)
fn main() {
    match lorem {
        // 符合
        ipsum => { 
            foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
        }
        dolor => println!("{}", sit),
        // 符合
        sit => foo(
            "foooooooooooooooooooooooo",
            "baaaaaaaaaaaaaaaaaaaaaaaarr",
            "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz",
            "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx",
        ),
    }
}
fn foo() {

    match foo {
        // 符合
        "foo" | "bar" => {}
        "baz"
        | "something relatively long"
        | "something really really really realllllllllllllly long" => println!("x"),
        "qux" => println!("y"),
        _ => {}
    }
}

导入模块分组应该具有良好的可读性

  • 导入同一模块的类型,应该置于同一个块内
  • 模块导入应该按以下规则进行分组:
    • 导入来自 std、core 和 alloc的模块需要置于前面
    • 导入来自 第三方库的模块 应该置于中间
    • 导入来自本地 self、super和crate前缀的模块,置于后面
  • 分组内使用字典序进行排序

#![allow(unused)]
fn main() {
    // 符合
    use foo::{
        a, b,
        b::{f, g},
        c,
        d::e,
    };
    use qux::{h, i};
    
    // 符合
    use alloc::alloc::Layout;
    use core::f32;
    use std::sync::Arc;

    use broker::database::PooledConnection;
    use chrono::Utc;
    use juniper::{FieldError, FieldResult};
    use uuid::Uuid;

    use super::schema::{Context, Payload};
    use super::update::convert_publish_payload;
    use crate::models::Event;
}

声明宏分支应该具有良好的可读性

  • 在声明宏中,模式匹配分支(=> 左侧)应该使用紧凑格式(format_macro_matchers=true)。
  • 而分支代码体(=> 右侧) 使用宽松格式。
fn main() {
// 当 `format_macro_matchers=true` 且 `format_macro_bodies=true`
macro_rules! foo {
    // 符合:匹配分支紧凑格式, `$a:ident` 和 `$b:ty` 各自配对
    ($a:ident : $b:ty) => {
        $a(42): $b; // 在代码体内,则宽松一点
    };
    // 符合
    ($a:ident $b:ident $c:ident) => {
        $a = $b + $c;
    };
}
}

具名结构体字段初始化时不要省略字段名

  • 省略字段名的时候需要注意变量名和字段名保持一致
  • 变量名和字段名不一致的情况下,不要省略字段名

struct Foo {
    a: u32,
    y: u32,
    z: u32,
}

fn main() {
    let x = 1;
    let y = 2;
    let z = 3;
    // 符合
    let a = Foo { a: x, y: y, z: z };
}

extern 外部函数需要显式指定 C-ABI

  • 当使用 extern 指定外部函数时,建议显式指定 C-ABI
  • 虽然 extern 不指定的话默认就是 C-ABI,但是 Rust 语言显式指定是一种约定俗成。

#![allow(unused)]
fn main() {
// 符合
extern "C" {
    pub static lorem: c_int;
}

extern "Rust" {
    type MyType;
    fn f(&self) -> usize;
}
}

不要将派生宏中多个不相关的特质合并为同一行

  • 不要将派生宏(Derive)中多个特质(trait)合并为同一行,这样可以增加代码可读性,明确语义。
  • 说明: rustfmt 并不会识别哪些特质相关,所以需要开发者手工指定好。
#![allow(unused)]
fn main() {
// 符合
    #[derive(Eq, PartialEq)]
    #[derive(Debug)]
    #[derive(Copy, Clone)]
    pub enum Foo {}
}