はじめに
プログラミングを学ぶ上で、良いコードの書き方を知ることは非常に重要です。今回は、Rustで良いコードを書くための強力な味方、Clippyについて学んでいきましょう。プログラミング初心者の方から、他の言語からRustに移ってきた方まで、きっと新しい発見があるはずです。
私も最近、Rustに関する素晴らしい本を読んでいます。「Effective Rust」と「Idiomatic Rust」は、Rustらしい書き方やデザインパターンについて詳しく解説していて、とても勉強になります。ただ、正直なところ、本を読んだだけでは私自身なかなか良いコードが書けず、ツールでのレビューで「これRustらしくないよね」とよく指摘されています。そのたびに勉強させられています。きっと同じような経験をされている方も多いのではないでしょうか。
そんな中で私の強い味方になっているのが、今回紹介するClippyです。本で学んだ内容を実践しようとするとき、Clippyは具体的なアドバイスをくれる、とても親切な存在です。特に「ここがRustらしくない」と言われたときの改善方法を、実例を挙げて教えてくれるのが心強いです。
Rust 標準 linter: Clippy
プログラミング言語には、よくある間違いや非推奨の書き方をチェックして警告を発してくれる、lintというプログラムがあります。元々はC言語をチェックするものでしたが、現在では様々な言語のためのlinterが作られています。Lint Nightなんてイベントもあります。
Rustには言語標準のlinterがあり、その名をclippyと言います。使い方は極めて簡単で、cargoツールチェインがインストールされていれば、下記のようにインストールして、
$ rustup component add clippy
下記のコマンドをcrateのフォルダで実行するだけです。
$ cargo clippy
Clippyのlinterとしての特徴
linterはコードの品質を向上するために、多くの現場で使われているツールですが、実際には厳しすぎるルールや、実際の問題にそぐわないものも多くあります。これを偽陽性(false positive)の検出と呼びます。
通常は設定ファイルや特殊なコメントをコードに埋め込むことによって、特定のlintの有効・無効を切り替えることになります。これは無視できない労力で、linterのバージョンを更新するたびに新たなルールに対応する必要が出てきたり、コメントによってコードが汚くなったりするデメリットもあります。
Clippyの特徴は、デフォルトの設定でもそのような偽陽性の警告が少なく、実際にコードの品質が向上したり、プログラマとしての知識が得られるのを実感できるような警告が多いということです。
実践的な例
例えば、次のような関数を見てください:
fn sum_squares(values: &Vec<i32>) -> i32 { values.iter().fold(0, |acc, value| acc + value * value) }
この関数は問題なく動きますが、Idiomatic Rust(慣用的なRustコード)ではありません。
Clippyは、次のような親切な警告を出してくれます:
Checking test001 v0.1.0 (/Users/nwiizo/git/workspace_2024/clippy/test001) warning: writing `&Vec` instead of `&[_]` involves a new object where a slice will do --> src/main.rs:1:24 | 1 | fn sum_squares(values: &Vec<i32>) -> i32 { | ^^^^^^^^^ help: change this to: `&[i32]` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg = note: `#[warn(clippy::ptr_arg)]` on by default
これは、&Vec
よりも&[]
のほうが汎用性が高いということを教えてくれています。Vec<T>
は&[T]
に暗黙に変換されるので、わざわざVec<T>
で宣言するということは、使える範囲を狭めるだけで何のメリットもないのです。
この関数は、機能性を全く損なわずに、次のように書き直すことができます:
fn sum_squares(values: &[i32]) -> i32 { values.iter().fold(0, |acc, value| acc + value * value) }
ミュータブル参照の場合
ところで、引数の型がミュータブル参照であった場合は話が別です。&mut Vec<T>
と&mut [T]
ではできることが異なります。
次のように、引数のベクター型のサイズを変えるような関数は、ミュータブルスライスで置き換えることはできません:
fn append_square(values: &mut Vec<i32>) { values.push(values.iter().fold(0, |acc, value| acc + value * value)); } // 使用例 let mut vv = vec![1,2,3]; append_square(&mut vv); assert_eq!(vec![1,2,3,14], vv);
このため、Clippyはミュータブル参照に対しては警告を発しません。これは、Clippyが文脈を理解して適切な判断を下せることを示す良い例です。
neovim/nvim-lspconfig での設定
VSCodeやCursor は知らないがこちらの設定でneovim は設定できる。
{ "neovim/nvim-lspconfig", config = function() require("nvchad.configs.lspconfig").defaults() local lspconfig = require "lspconfig" lspconfig.rust_analyzer.setup { settings = { ["rust-analyzer"] = { checkOnSave = { command = "clippy", extraArgs = { "--all", "--", "-W", "clippy::all" }, }, }, }, } require "configs.lspconfig" end, },
おわりに
Clippyは、より良いRustプログラムを書くことができるように導いてくれる、優しい先生のような存在です。もちろん、Clippyも完璧ではなく、時には偽陽性の検出もありますが、それは人間でも同じことです。
より良いRustの書き方を学び、コードの品質を向上させ、プログラミングの知識を深められるClippyは、人間のレビュアーとは違って何度指摘されても評価が下がることのない、心強い味方となってくれます。
という利点があります。ぜひ、みなさんも日々のRustプログラミングにClippyを取り入れてみてください。疑問に思ったClippyの警告は、その都度調べてみることをお勧めします。そうすることで、Rustの理解がより深まっていくはずです。Effective Rustに関しては日本語の本が出ているので興味があれば読んでみても良いと思う。
個人的に良かった記事
業務 におけるRust の記事を読んだがどちらの記事もとても良かった。