100日後にRustをちょっと知ってる人になる: [Day 69]コード整形: rustfmt
Day 69 のテーマ
Day 68 では、Blessed.rs で紹介されている Lint ツールの clippy の使い方を見てみました。今日も引き続き、Blessed.rs で紹介されているクレートを見てみたいかなと思います。

というわけで、開発ツールで紹介されている コードフォーマットツールの rustfmt について今日は見てみたいかなと思います。
rustfmt
以下が rustfmt のリポジトリです。見てもらえば分かるように、先日の Lint ツールの clippy 同様に、この rustfmt も rust-lang の配下におかれる Rust の公式なコードフォーマットツールです。
この rustfmt は、コードのスタイルガイドラインに従って、作成するコードを整形するために用いられるものです。
デフォルトでは、次のスタイルガイドに準拠しているようです。
インデントやライン幅、空白行、またモジュールレベルの項目やステートメント、式、型などの項目でそれぞれフォーマットを定義しています。
インストール
rustup component add rustfmt
これで、次のコマンドでコードフォーマットを実行できます。
rustfmt
あるいは
cargo fmt
コードフォーマット実行
次のような、コードスタイルがおかしなサンプルコードを作成してみます。
fn main(){
for n in 0..10{
println!("{n}: Hello, Rust!");
}
}
インデントが崩れていて、ひと目でおかしなコードだとわかりますよね。ただ、コード的には間違いはないので実行はできます。
$ cargo run
0: Hello, Rust!
1: Hello, Rust!
2: Hello, Rust!
3: Hello, Rust!
4: Hello, Rust!
5: Hello, Rust!
6: Hello, Rust!
7: Hello, Rust!
8: Hello, Rust!
9: Hello, Rust!
あと、clippy も実施してみましたが特にエラーは発生しませんでした。
ここで、cargo fmt を実行してみます。特にメッセージが表示されるわけではないのですが、実行後にソースコードを見てください。
$git diff
-fn main(){
-for n in 0..10{
-println!("{n}: Hello, Rust!");
+fn main() {
+ for n in 0..10 {
+ println!("{n}: Hello, Rust!");
+ }
}
-}
つまり、以下のようにコードが整形されていました。
fn main() {
for n in 0..10 {
println!("{n}: Hello, Rust!");
}
}
ソースコードをコミットする前には必ず rustfmt を実行するという習慣をつけておくといいかもしれないですね。
スタイルガイド設定
先にも書いていたように、rustfmt では、RFC で規定された Rust スタイルガイドに準拠したスタイルになっています。利用可能なスタイルオプションは、次のコマンドで確認出来ます。
rustfmt --help=config
スタイルガイドをカスタマイズしたい場合は、rustfmt.toml というファイルを作成して定義を行うことが可能です。
次のコマンドを実行することで、デフォルト設定を書き出してくれるので、それをベースにカスタマイズするといいと思います。
rustfmt --print-config default rustfmt.toml
オプション一覧
| option名 | 設定値 | 説明 |
|---|---|---|
| verbose | <boolean> Default: false |
Use verbose output |
| skip_children | <boolean> Default: false |
Don't reformat out of line modules |
| [max_width] | <unsigned integer> Default: 100 |
Maximum width of each line |
| ideal_width | <unsigned integer> Default: 80 |
Ideal width of each line |
| [tab_spaces] | <unsigned integer> Default: 4 |
Number of spaces per tab |
| [fn_call_width] | <unsigned integer> Default: 60 |
Maximum width of the args of a function call before falling back to vertical formatting |
| [struct_lit_width] | <unsigned integer> Default: 16 |
Maximum width in the body of a struct lit before falling back to vertical formatting |
| newline_style | [Windows| Unix| Native] Default: Unix |
Unix or Windows line endings |
| [fn_brace_style] | [AlwaysNextLine| PreferSameLine| SameLineWhere] Default: SameLineWhere |
Brace style for functions |
| [item_brace_style] | [AlwaysNextLine| PreferSameLine| SameLineWhere] Default: SameLineWhere |
Brace style for structs and enums |
| [impl_empty_single_line] | <boolean> Default: true |
Put empty-body implementations on a single line |
| [fn_empty_single_line] | <boolean> Default: true |
Put empty-body functions on a single line |
| [fn_single_line] | <boolean> Default: false |
Put single-expression functions on a single line |
| [fn_return_indent] | [WithArgs| WithWhereClause] Default: WithArgs |
Location of return type in function declaration |
| [fn_args_paren_newline] | <boolean> Default: true |
If function argument parenthesis goes on a newline |
| [fn_args_density] | [Compressed| Tall| CompressedIfEmpty| Vertical] Default: Tall |
Argument density in functions |
| [fn_args_layout] | [Visual| Block| BlockAlways] Default: Visual |
Layout of function arguments |
| [fn_arg_indent] | [Inherit| Tabbed| Visual] Default: Visual |
Indent on function arguments |
| [type_punctuation_density] | [Compressed| Wide] Default: Wide |
Determines if '+' or '=' are wrapped in spaces in the punctuation of types |
| [where_density] | [Compressed| Tall| CompressedIfEmpty| Vertical] Default: CompressedIfEmpty |
Density of a where clause |
| [where_indent] | [Inherit| Tabbed| Visual] Default: Tabbed |
Indentation of a where clause |
| [where_layout] | [Vertical| Horizontal| HorizontalVertical| Mixed] Default: Vertical |
Element layout inside a where clause |
| [where_pred_indent] | [Inherit| Tabbed| Visual] Default: Visual |
Indentation style of a where predicate |
| [where_trailing_comma] | <boolean> Default: false |
Put a trailing comma on where clauses |
| [generics_indent] | [Inherit| Tabbed| Visual] Default: Visual |
Indentation of generics |
| [struct_trailing_comma] | [Always| Never| Vertical] Default: Vertical |
If there is a trailing comma on structs |
| [struct_lit_trailing_comma] | [Always| Never| Vertical] Default: Vertical |
If there is a trailing comma on literal structs |
| [struct_lit_style] | [Visual| Block] Default: Block |
Style of struct definition |
| [struct_lit_multiline_style] | [PreferSingle| ForceMulti] Default: PreferSingle |
|
| [enum_trailing_comma] | <boolean> Default: true |
Put a trailing comma on enum declarations |
| report_todo | [Always| Unnumbered| Never] Default: Never |
Report all, none or unnumbered occurrences of TODO in source file comments |
| report_fixme | [Always| Unnumbered| Never] Default: Never |
Report all, none or unnumbered occurrences of FIXME in source file comments |
| [chain_base_indent] | [Inherit| Tabbed| Visual] Default: Visual |
Indent on chain base |
| [chain_indent] | [Inherit| Tabbed| Visual] Default: Visual |
Indentation of chain |
| [reorder_imports] | <boolean> Default: false |
Reorder import statements alphabetically |
| [single_line_if_else] | <boolean> Default: false |
Put else on same line as closing brace for if statements |
| [format_strings] | <boolean> Default: true |
Format string literals where necessary |
| [force_format_strings] | <boolean> Default: false |
Always format string literals |
| [chains_overflow_last] | <boolean> Default: true |
Allow last call in method chain to break the line |
| [take_source_hints] | <boolean> Default: true |
Retain some formatting characteristics from the source code |
| [hard_tabs] | <boolean> Default: false |
Use tab characters for indentation, spaces for alignment |
| [wrap_comments] | <boolean> Default: false |
Break comments to fit on the line |
| [normalise_comments] | <boolean> Default: true |
Convert /* */ comments to // comments where possible |
| [wrap_match_arms] | <boolean> Default: true |
Wrap multiline match arms in blocks |
| [match_block_trailing_comma] | <boolean> Default: false |
Put a trailing comma after a block based match arm (non-block arms are not affected) |
| [match_wildcard_trailing_comma] | <boolean> Default: true |
Put a trailing comma after a wildcard arm |
| write_mode | [Replace| Overwrite| Display| Diff| Coverage| Plain| Checkstyle] Default: Replace |
What Write Mode to use when none is supplied: Replace, Overwrite, Display, Diff, Coverage |
Day 69 のまとめ
rustfmt はコードを自動に整形してくれてとても便利なツールだとわかりました。都度都度コードを整形しながらキレイな状態で保っていくとよさそうかなと思います。
また、PR するときなども礼儀として整形されたコードでコミットしたいかなと思いました。
Discussion