格式
使用 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 {}
}