Rustプログラミングのための環境構築

Author: blackenedgold
Published: 2020-12-04
Last Modified: 2021-06-30
GitHub Source: md

概要

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/binexec-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-hookcargo-minor-mode を追加します。 これで rust-mode が起動するときは cargo-minor-mode がonになります。

(use-package lsp-mode
  :ensure t
  ; ...)

lsp-modeのパッケージを使う宣言です。なければインストールします。

  :hook (rust-mode . lsp)

rust-mode-hooklsp を追加します。 これで rust-mode が起動するときは lsp-mode がonになります。

  :bind ("C-c h" . lsp-describe-thing-at-point)

lsp-mode では C-c hlsp-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-filesrc/main.rs を選択)。 lsp-modeがワークスペースをインポートするか尋いてくるので i と入力してインポートします。

プロジェクトを開いたときの様子

初回はrust-analyzer/rlsの初期化に少し時間がかかります。

cargo new で動くプロジェクトが作られているのでcargo-minor-modeのキーバインドを使って走らせてみましょう。

C-c C-c C-r です。

C-c C-c C-rで走らせたところ

下のウィンドウに “Hello world!” と表示されていますね。成功です。

それではlsp-modeの補完を試してみましょう。 ファイルの先頭に use std::collections::HashMap; と入力しようとしてみて下さい。 するとcompanyで補完がされるはずです。

use std::collections::HashMap;を補完しているところ

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

cargo addを使っているところ

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

今追加したregexパッケージを使ってみましょう。 let regex = Regex::new("foo.*").unwrap(); と入力しようとしてみて下さい。

regex::Regexを補完しているところ

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

続いて関連関数の new を入力するシーンでももちろん補完されます。

Regex::newを補完しているところ

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

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 を起動する
  • cargo-minor-modeのcheck(C-c C-c C-K
    • Rustのlinterの cargo clippy を起動する
  • 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 を起動する
  • 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 の空の腕の部分にカーソルを合わせた状態です。

Actionが表示された画面

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

Actionはクリックできることを示す画面

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

Actionを適用すると以下のように補完されます。

Actionを適用したあとの画面

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

Actionの適用後、最後まで書いた画面

あとはこれを埋めてコードを完成させましょう。

解説は以上です。

  1. 毎週のリリースの他に毎晩のプレリリースもあり、そちらも通知されてしまいます。頑張って無視しましょう。