[rust-analyzer]VScodeでRustの補完機能を使いたいのに動かない

6 min read読了の目安(約5500字

経緯(読み飛ばしていいです)

最近C系言語を業務で扱うことがあり、使い方が分からないわけではないのですがメモリ解放したっけとかポインタ、参照渡し等々モダンプログラミング言語からしたら非常に面倒なことをしているように見えるかもしれません。(存外私はキライではないです。)

CやC++の代替を目指しているらしいRustの存在を知ってはいましたが一度も使ったことはありません。

折角業務で比較対象となるC系言語を扱っているので、それらと比較したときRustはどれくらい優れたモノなのか試してみよう、と思ったのが今回Rustに触れるきっかけとなりました。

本題

私はPC内にコンパイラだとか色々と入れまくって環境が汚れるのが好きではないです。
基本Docker内に環境を構築して、その中で遊ぶことがほとんどです。

Dockerで環境構築すること自体はなんら大した作業ではありません。(家でやる分には)

以下のような構成でちょちょいのちょいとRustを実行できる環境を構築しました。

Dockerfile
FROM rust:1.48-buster
RUN apt-get update & apt-get install git
RUN cargo install cargo-edit #←予め入れて置いた方がいい、結構重め
docker-compose.yml
version: "3"

services:
  rust-app:
    container_name: "rust-app"
    build:
      context: .
      dockerfile: Dockerfile
    tty: true
    volumes:
      - ..:/workspace.rust/src/app
.devcontainer.json
{
    "name": "Learning Rust",
    "dockerComposeFile": "docker-compose.yml",
    "service": "rust-app",
    "workspaceFolder": "/workspace.rust",
    // Use this environment variable if you need to bind mount your local source code into a new container.
    "remoteEnv": {
        "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}",
    },
    // Set *default* container specific settings.json values on container create.
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash"
    },
    // Add the IDs of extensions you want installed when the container is created.
    "extensions": [
        "ms-azuretools.vscode-docker",
    ],
    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],
    // Use 'postCreateCommand' to run commands after the container is created.
    // "postCreateCommand": "docker --version",
    // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
}

さて、これだけでRemote-Contaienrsでコンテナに接続してRustで遊べます。
しかし、やはりRustの拡張機能は欲しい…。
補完がないのはちと厳しいです。

拡張機能として最初は以下を選択しました。

https://marketplace.visualstudio.com/items?itemName=rust-lang.rust

これで補完も効いて、カーソルを当てればdocも見れて大助かりだ!!!

と思っていた時期もありました…。

あまり使えない

全然補完してくれないし、何だったら外部クレートをcargo add xxxxでインストールしても補完機能が反応しないし、そもそもインストールできているのに読み込めていませんでした。
コンテナを再度開きなおすと(Remote-Containers: Reopenコマンドです)読み込めましたが、いちいち開きなおすのも面倒です。

拡張機能の説明をよく読んでみると以下の説明がありました。

This extension is deliberately conservative about snippets and doesn't include too many. If you want more, check out Trusty Rusty Snippets.

あまり補完向きではないようです。

そこで別の拡張機能がないか探そうと思いましたが、サイトの説明文にもう一つのlanguage serverが紹介されていました。

Rust support is powered by a separate language server - either by the official Rust Language Server (RLS) or rust-analyzer, depending on the user's preference. If you don't have it installed, the extension will install it for you (with permission).

rust-analyzerというものもあるらしいです。

またしても機能せず

早速rust-analyzerを拡張機能としてインストールしました。

https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer
versionからも分かるように未だ開発中のlanguage serverですが、RLSより補完機能もしかっかりしているらしいです。

ですがまたしても動きません。
それどころかエラーを吐くようになってしまいました。

rust-analyzer failed to discover workspace

ワークスペースが見つかりません…?

原因調査

rust-analyzerを導入したときのフォルダ構成は以下のような感じです。
一つのワークスペースに複数プロジェクトがある状態です。
そしてワークスペースは確かに存在しているはずです。
.devcontainer.jsonで /workspace.rustと明示的に指定しています。
一体何が悪いのでしょうか。

workspace.rust/
  └src/
    └app/
      ├sample1/
      │  ├src/
      │  │  └main.rs
      │  └Cargo.toml
      ├sample2/
      │  ├src/
      │  │  └main.rs
      │  └Cargo.toml
      ├sample3/
      │  ├src/
      │  │  └main.rs
      │  └Cargo.toml
      └sample4/
         ├src/
         │  └main.rs
         └Cargo.toml

原因

結論から申し上げますと、一つのワークスペースに複数プロジェクトが存在していることが原因でした。

以下、原因判明に至ったgithubのIssueへのリンクです。

https://github.com/rust-analyzer/rust-analyzer/issues/1834

現段階では単一ワークスペースに一プロジェクトではないと動いてくれないようです。
ただ複数サポートの準備自体は整いつつあるらしく、まだ実証が不十分なため実装されていないとのことです。

修正&改善

結果として以下のような構成になりました。

Dockerfile
FROM rust:1.48-buster
RUN apt-get update & apt-get install git
RUN rustup component add rust-src #新規追加、rust-analyzer導入時に必要
RUN cargo install cargo-edit #←予め入れて置いた方がいい、結構重め
docker-compose.yml
version: "3"

services:
  rust-app:
    container_name: "rust-app"
    build:
      context: .
      dockerfile: Dockerfile
    tty: true
    volumes:
      - ..:/workspace.rust
.devcontainer.json
{
    "name": "Learning Rust",
    "dockerComposeFile": "docker-compose.yml",
    "service": "rust-app",
    "workspaceFolder": "/workspace.rust",
    // Use this environment variable if you need to bind mount your local source code into a new container.
    "remoteEnv": {
        "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}",
    },
    // Set *default* container specific settings.json values on container create.
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash"
    },
    // Add the IDs of extensions you want installed when the container is created.
    "extensions": [
        "ms-azuretools.vscode-docker",
	/// rust-analyzerを設定 ///
	"matklad.rust-analyzer",
    ],
    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],
    // Use 'postCreateCommand' to run commands after the container is created.
    // "postCreateCommand": "docker --version",
    // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
}

ついでにフォルダ構成をシンプルな形に修正しました。

workspace.rust/
  └sample1/
      ├src/
      │  └main.rs
      └Cargo.toml

いざコンテナをリビルドしてコンテナに接続すると…

無事rust-analyzerが反応してくれました。