概要
EmacsでのRust言語をプログラミングする際の環境構築について示します。
このページは以下の記事をemacs-jpのために再編集し投稿したものです。
ツールのインストール
Rustupによるrustツールチェーンのセットアップは済んでいるものとして、他のツールの準備方法を案内します。
フォーマッタ、リンタ
公式で配布されているrustfmt(フォーマッタ)とclippy(リンタ)が鉄板です。 インストールは…既に上記の方法でインストールされています。 確認してみましょう。
$ which rustfmt
/home/shun/.cargo/bin/rustfmt
$ which cargo-clippy
/home/shun/.cargo/bin/cargo-clippy
もしインストールされていなかったら下記のコマンドでインストールできます。
$ rustup component add rustfmt clippy
LSPサーバ
LSPはマイクロソフトが提唱した言語処理系とエディタ/IDEがやりとりするためのプロトコルです。 ざっくり言うとLSPをサポートしている言語ならEmacsがEclipseやIntelliJ並にリッチな環境になります。
さて、RustのLSPサーバの状況なのですが、ツールが2つあります。
1つがrlsで現行の公式推奨のLSPサーバです。
もう1つがrust-analyzerで、一応実験的な実装とされています。 しかし出来がよく、rust-analyzerを公式のツールにしようとする動きもあります。
ここでは両方のインストール方法を紹介するので好きな方をインストールしてみて下さい。
RLS
rustupでインストールできます。
$ rustup component add rls
rust-analyzer
毎週バイナリリリースがGitHubに作られるので、そこからダウンロードして使います。
rust-analyzerを /PATH/TO/rust-analyzer に保存するとして、以下のようなコマンドを毎週叩くことになるでしょう。(自分の環境向けのバイナリをダウンロードしてください)
# x86_64 Linux
$ curl -sL https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | zcat > /PATH/TO/rust-analyzer
# macOS
$ brew install rust-analyzer
# x86_64 Windows
$ curl -sL https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-pc-windows-msvc.gz | zcat > /PATH/TO/rust-analyzer.exe
rust-analyzerのマニュアルも参考にして下さい。
rust-analyzerは毎週更新されるので使う方はGitHubの右上にある[Watch]から[Custom]の[Releases]にチェックを入れて、毎週のリリースの通知を受け取るとよいでしょう1
その他
cargo install でRust製ツールをインストールできます。
例えばCargo.tomlをコマンドから編集できるcargo-editは以下のコマンドでインストールできます。
$ cargo install cargo-edit
あとで紹介するcargo-minor-modeでもサポートがあるので cargo-edit のインストールを推奨します。
Emacsのセットアップ
Rustの開発で一番使われているエディタはVSCodeらしいですが、LSPのおかげでEmacsでも遜色なく開発できます。 使うパッケージは以下です。
- rust-mode: Rustのメジャーモード
- lsp-mode: 上述のLSPのEmacsサポート。Rustサポートも同梱されます。
- lsp-ui: LSPの表示レイヤー
- cargo: CargoをEmacsから呼び出せるキーバインド
- (company)
- (yasnippet)
設定の方法はいくつか流儀があるかと思いますが、ここではuse-packageを使い以下のような設定をします。
(add-to-list 'exec-path (expand-file-name "/PATH/TO"))
(add-to-list 'exec-path (expand-file-name "~/.cargo/bin"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; #rust
(use-package rust-mode
:ensure t
:custom rust-format-on-save t)
(use-package cargo
:ensure t
:hook (rust-mode . cargo-minor-mode))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; #lsp
(use-package lsp-mode
:ensure t
:init (yas-global-mode)
:hook (rust-mode . lsp)
:bind ("C-c h" . lsp-describe-thing-at-point)
:custom (lsp-rust-server 'rust-analyzer))
(use-package lsp-ui
:ensure t)
1つずつ解説していきます。
(add-to-list 'exec-path (expand-file-name "/PATH/TO"))
rust-analyzerをインストールしたディレクトリを exec-path に加えます。
(add-to-list 'exec-path (expand-file-name "~/.cargo/bin"))
~/.cargo/bin を exec-path に加えます。cargoやrustfmtなどをEmacsから使うために必要です。
(use-package rust-mode
:ensure t
; ...
)
rust-modeのパッケージを使う宣言です。なければインストールします。
:custom rust-format-on-save t
ファイルを保存する度に rustfmt を適用します。
(use-package cargo
:ensure t
; ...
)
cargoのパッケージを使う宣言です。なければインストールします。
:hook (rust-mode . cargo-minor-mode)
rust-mode-hook に cargo-minor-mode を追加します。
これで rust-mode が起動するときは cargo-minor-mode がonになります。
(use-package lsp-mode
:ensure t
; ...)
lsp-modeのパッケージを使う宣言です。なければインストールします。
:hook (rust-mode . lsp)
rust-mode-hook に lsp を追加します。
これで rust-mode が起動するときは lsp-mode がonになります。
:bind ("C-c h" . lsp-describe-thing-at-point)
lsp-mode では C-c h に lsp-describe-thing-at-point を割り当てます。
因みに lsp-rust のデフォルトのLSPバックエンドはrust-analyzerです。
RLSを使う方は :custom (lsp-rust-server 'rls) などの設定が必要になるでしょう。
(use-package lsp-ui
:ensure t)
lsp-uiのパッケージを使う宣言です。なければインストールします。
その他お好みで設定して下さい。
設定した環境の使いかた
基本編
基本は自動で動いてくれます。ちょっとプロジェクトを作ってテストしてみましょう。
cargo new でプロジェクトを作ります。
$ cargo new test-project
Created binary (application) `test-project` package
プロジェクトをEmacsで開いてみましょう(find-fileで src/main.rs を選択)。
lsp-modeがワークスペースをインポートするか尋いてくるので i と入力してインポートします。

初回はrust-analyzer/rlsの初期化に少し時間がかかります。
cargo new で動くプロジェクトが作られているのでcargo-minor-modeのキーバインドを使って走らせてみましょう。
C-c C-c C-r です。

下のウィンドウに “Hello world!” と表示されていますね。成功です。
それではlsp-modeの補完を試してみましょう。
ファイルの先頭に use std::collections::HashMap; と入力しようとしてみて下さい。
するとcompanyで補完がされるはずです。

次にツールでインストールしたcargo-editを使っていみましょう。cargo-minor-mode経由で使えます。
C-c C-c C-a RET regex RET と入力してみて下さい。

cargo-editでインストールされたサブコマンド、 cargo add を使ってパッケージを追加してくれます。
これは Cargo.toml の [dependencies] に regex = "最新のバージョン" を追記する指示です。
どうやらlsp-modeが追記を読み込んでくれないようなので M-x lsp-restart-workspace でリロードしましょう。
今追加したregexパッケージを使ってみましょう。
let regex = Regex::new("foo.*").unwrap(); と入力しようとしてみて下さい。

補完候補がでてきます。
このうち regex::Regex を選択するとファイルの先頭に use regex::Regex; が自動で追記されます。
なんとオートインポートまでされるんですね。
なんかドキュメントがオーバーレイ表示されて邪魔な場合は M-x lsp-ui-doc-hide とでもしてみて下さい。
続いて関連関数の new を入力するシーンでももちろん補完されます。

それではこれの型検査(cargo check)をしてみましょう。
cargo-minor-modeの C-c C-c C-k を使います。

未使用アイテムの警告が出て、エラーが0なのでチェックは通っているようですね。
発展編
普段の開発でよく使う機能を紹介します。
- LSPの補完
- LSPの定義ジャンプ(
M-.)と元の場所に戻る(M-,) - LSPのActions(
s-l a a) - cargo-minor-modeのcheck(
C-c C-c C-k)- Rustの型検査だけやってくれる
cargo checkを起動する
- Rustの型検査だけやってくれる
- cargo-minor-modeのcheck(
C-c C-c C-K)- Rustのlinterの
cargo clippyを起動する
- Rustのlinterの
- cargo-minor-modeのtest(
C-c C-c C-t)- テストを走らせる
cargo testを起動する
- テストを走らせる
- cargo-minor-modeのadd(
C-c C-c C-a)- cargo-editプラグインの依存パッケージ追加コマンド
cargo addを起動する
- cargo-editプラグインの依存パッケージ追加コマンド
C-c C-c C-kのあとのM-g M-n/M-g M-p(next-error/previous-error)- Cargoの出したエラーの起きたソースの位置に飛べる
そんなに多くないので簡単に覚えられるでしょう。 このうち、Actionについて知らないと分からないと思うので説明しておきます。
LSPにはActionというものがあるようです。 コードの特定の場所にカーソルを合わせたときにLSPサーバがActionを提示できるならそれが表示されます。 例えば変数のリネームなどです。
rust-analyzerは結構面白いActionを提示してくれます。
例えば以下は match の空の腕の部分にカーソルを合わせた状態です。

右上に赤字で表示されているようにパターンが足りていないのでエラーになります。 その下に “Fill match arms” とありますね。これがActionです。

このActionを実行してみましょう。マウスを使ってクリックするか、s-l a a と入力すると実行できます。
上の画像はマウスカーソルを合わせたところです。Emacsでもマウス操作ができるんですね。
なお、yasnippetが必要なので yas-minor-mode がonになってるかは確認しましょう。
Actionを適用すると以下のように補完されます。

今回なmatchに必要な Ok と Err が補完されています。
型までみて賢く動作してくれるんですね。すごいですね。

あとはこれを埋めてコードを完成させましょう。
解説は以上です。
-
毎週のリリースの他に毎晩のプレリリースもあり、そちらも通知されてしまいます。頑張って無視しましょう。 ↩
