8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

48歳エンジニアのRust学習日記 #1 〜Vimの矜持、VSCodeに膝を折る〜

8
Posted at

密林からの遅延通知

本物のカニは来たのに、カニ本は来ない。世の中とは、だいたいそういうものだ。

待っていれば届く。わかっている。

だが待てない。「本が届かないから勉強できません」などという言い訳が、令和の上に厳しい若手社員に通じるだろうか? いや、通じない。

「本がなければ、体で覚えればいいじゃない」

待っている間に、Hello World くらいは書けるだろう。

というわけで今日は、カニ本が密林から届くまでの間に、25年選手の経験と勘を頼りに Rust を始めてみようと思う。

儀式のない導入は信用できない(※個人の感想です)

(※ここから先、コマンドは環境によって微妙に違うので、雰囲気だけ受け取ってください。)

Rust の公式サイトを開くと、こんなコマンドが書いてあった。

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

……は?

これで環境構築が終わる?
ウソだろ。

本物のプログラム環境というのは、もっとこう、儀式的 なものだ。

まず apt install で複数のパッケージをインストールする。
次に、依存ライブラリのバージョンを確認して、競合がないか祈る。
それから CMakeLists.txt みたいな設定ファイルを書いて、
環境変数を山ほど設定して、
パスを通して、
軽く Hello World を書いて、
cmake を実行して、
エラーを読んで、
また設定を直して、
やっとビルドコマンドが叩けるようになる。

それが、プログラムというものだ。

たった一行の curl で環境が整うなんて、そんな楽な話があるわけがない

でも、一応試してみる。

ターミナルに貼り付けて、Enter。

info: downloading installer
...

……走ってる。

info: profile set to 'default'
info: default host triple is x86_64-unknown-linux-gnu
...

……インストールされてる。

Rust is installed now. Great!

……終わった?

冗談だろ……。

本当に終わったのか?

rustc --version を打ってみる。

rustc 1.92.0 (ded5c06cf 2025-12-08)

動いてる。

なんだこれ。
こんなに簡単でいいのか。

【技術解説:なぜ Rust は curl 一発で済むのか】

Rust の入口がやたらとスムーズなのは、rustup という “公式の世話係” が最初から用意されているからだ。

  • rustup:Rust のツールチェーン(rustccargo)をまとめて管理する
  • つまり Rust は「まず公式が面倒を見る」という思想で始まる

Python の pyenv や Node.js の nvm のような便利枠が、最初から標準装備されている。
この時点で、私の好きな「儀式」はすでに不要になっていた。

……なお、詰まりやすいポイントと最短手順は、最後にまとめて置いておく(未来の私へ)。


rustup:長い名前は信用しない病

次に、rustup --version を打ってみた。

rustup 1.28.2 (e4f3ad6f8 2025-04-28)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.92.0 (ded5c06cf 2025-12-08)`

rustup か。

Rust SetupRust Startup ?。

まあ、なんでもいい。それでいい。 少なくとも、ちゃんと動く。

最近のコマンドは、なんでもかんでも「リーダビリティ」とか言って、
install-rust-toolchain-manager だの
rust-environment-configurator だの、
ただの英文みたいな名前 をつけたがる。

違うんだよ。

コマンドなんて、見た目で意味がわかる必要はないんだ。
短くて、指に馴染めばいい。

ls, cd, rm, cp ——これが美しいんだ。

rustup も同じ。
短くて、覚えやすくて、タイプしやすい。

短さの美学が、ここにはある。

rustc、名乗りが正しい

次に、コンパイラを確認する。

rustc

これこれ。

コンパイルコマンドは、こうじゃないとな。

gcc (GNU C Compiler)
g++ (GNU C++ Compiler)
javac (Java Compiler)

そして rustc (Rust Compiler)。

完璧だ。

伝統に則っている。
余計な装飾がない。

これなら信用できる。

Hello World なのに、もう知らない

さて、いよいよコードを書く。

$ cargo new hello_rust
$ cd hello_rust

src/main.rs が生成される。

開いてみると、こんなコードが書いてあった。

fn main() {
    println!("Hello, world!");
}

……。

ちょっと違和感があるものの、まあ良い。
詳しいことは本が届いてから勉強するとして、とりあえず動かしてみよう。

公式サイトによると、Rustup をインストールすると、 Cargo というビルドツール兼パッケージマネージャーのような便利なものが入ると書かれており、どうもこいつを使うとビルドや実行ができるそうだ。

$ cargo build
   Compiling hello_rust v0.1.0 (/home/abe/pepperoni/hello_rust)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.83s

ほーん。

$ cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
     Running `/home/abe/pepperoni/hello_rust/target/debug/hello_rust`
Hello, world!

おぉ……、動いた動いた(ニンマリ)

やっぱりどんなテクノロジーを学ぶ時もこの最初の瞬間が一番楽しいよな。

Programming is fun again! というやつだ。

とにかく、

  • main.rs にコードを書いて
  • cargo build でビルドして
  • cargo run で動かす

これだけ分かれば、今の時点では十分だ。

そして、ベテランの勘というやつで少しコードを書いて遊んでみるものの…

数分後、既におじさんには色々と腑に落ちない部分がある

第一の違和感: fn main()

私の指は、反射的にこう動く。

int main() {

静的型付け言語なのに、なんで戻り値の型を書かないんだ?

C でも C++ でも Java でも、main 関数は int を返す。
それが常識だった。

でも Rust は fn main() と書く。

なんで?

脳内で推論する。

「……ああ、たぶん Rust の main は何も返さないんだな」
「void main() 的な?」
「でも void って書かないんだな」
「fn って function の略か」
「まあ、そういうもんなんだろう」

納得はしていないが、とりあえず受け入れる


【技術解説:戻り値はどこへ消えた?】

C/C++ なら int main() だ。プログラムは OS に終了コード(通常は 0)を返して終わるのが礼儀だろう。

Rust の main はデフォルトで ユニット型 () を返す。

fn main() -> () {  // () は省略可能
    println!("Hello, world!");
}

ユニット型は「何も返さない」を表す型だ。C/C++ の void に近いが、Rust では「値」として扱われる。

終了コードを返したい場合は?

use std::process::ExitCode;

fn main() -> ExitCode {
    ExitCode::SUCCESS  // 0 を返す
}

または、Result 型を返すこともできる。

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // エラーが起きたら自動的に終了コード 1 が返る
    Ok(())
}

Rust は「正常終了」をデフォルトとして、明示的にエラーハンドリングが必要な場合だけ Result を使う設計になっている。

Result を返すと Err 時に非0終了コードになる。(かつエラーが表示される)

【return は書かなくていい(むしろ書かない)】

でもさ、だとするとこう書きたくなるよね? ふつう

use std::process::ExitCode;

fn main() -> ExitCode {
    println!("Hello, world!");
    return ExitCode::SUCCESS;
}

実はこの書き方でもビルドは通る。しかし、Rust の設計思想では、関数本体は ブロック式 として扱われ、ブロックは値を評価する。そのため、関数内の最後の式(セミコロンなし)の評価値が戻り値になる

return は、関数の途中で即座に制御フローを終了させたい場合にのみ使用される。

なお、式にセミコロンを付けるとその評価値は破棄され、結果としてブロックの評価値は unit 型 () になる。そのため、戻り値として使用できなくなる。

fn main() -> ExitCode {
    ExitCode::SUCCESS;
    // ブロックの評価値が () になり、main が要求する ExitCode と一致しないため、型チェックでエラーになる
}

第二の違和感: let x = 5;

次に、変数を定義してみる。

let x = 5;

型を書かない。

型推論 が働いているんだろう。

まあ、これは許せる。
C++ にも auto があるし、最近の言語は大体こうだ。

しかし、let したものを書き換えようとすると怒られる。

fn main() {
    let year = 2025;

    println!("I started learning Rust.");

    if is_learning_rust() {
        year += 1; // コンパイルエラー: cannot assign twice to immutable variable
    }

    println!("Now it's {}", year);
}

fn is_learning_rust() -> bool {
    // TODO: actually verify progress
    true
}

【技術解説:letmut

let は「不変な束縛」を作る。

let year = 2025;

これは単なる変数の宣言ではなく、「不変な束縛を作る」ことに当たるそうな。
すなわち、そのままでは値を変更することはできない。

値を変更する可能性があるときは以下のように mut を付けなければならない。

let mut year = 2025;

※ 「束縛」「所有権」「借用」といった概念については、正直まだちゃんと理解できていない。今は「mut を付けると書き換えられる」くらいの感覚でいるが、その辺はカニ本の実力が試されるところだと信じている。


第三の違和感: println!! は何だ

そして、最大の違和感。

println!("Hello, world!");

なんで ! をつけるんだ?

println は関数だろ?

なんで関数名の後ろに ! がつくんだ?

Kotlin では、!! は強制的に nullable を参照するやつだった。
でも、これは一個だし、そもそも引数を取ってる。

もしかして、これが Rust の正しい文法 なのか?

試しに、! を消してみる。

println("Hello, world!");

保存して、cargo build

   Compiling hello_rust v0.1.0 (/home/abe/pepperoni/hello_rust)
error[E0423]: expected function, found macro `println`
 --> src/main.rs:2:5
  |
2 |     println("Hello, world!");
  |     ^^^^^^^ not a function
  |
help: use `!` to invoke the macro
  |
2 |     println!("Hello, world!");
  |            +

For more information about this error, try `rustc --explain E0423`.
error: could not compile `hello_rust` (bin "hello_rust") due to 1 previous error

やっぱりエラーになった。

エラーメッセージを読む。

! を付けてマクロとして呼べ」

use the `!` to invoke the macro: `println!`

マクロ?

println は関数じゃなくて、マクロ なのか。

なるほど、だから ! がつくのか。

C のマクロは #define で定義するやつだったが、
Rust では ! で呼び出すらしい。

……わかるか、そんなもん。

まあいい。
これが Rust のルールなら、従うしかない。

! を元に戻して、cargo run

   Compiling hello_rust v0.1.0 (/home/abe/pepperoni/hello_rust)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
     Running `/home/abe/pepperoni/hello_rust/target/debug/hello_rust`
Hello, world!

動いた。


【技術解説:println! の「!」の正体】

println は関数ではない。マクロ だ。
最初、タイポかと思って ! を消したら、コンパイラに烈火のごとく怒られた。

なぜ関数ではなくマクロなのか?

C の printf のような可変引数を、型安全に扱うために、Rust は関数ではなくマクロという手段を選んでいる。
(※ printf を使ったことがある人なら、感覚的に分かると思う)

println!("x = {}", x);             // 引数2個
println!("x = {}, y = {}", x, y);  // 引数3個

これらは、コンパイル時に適切な関数呼び出しに展開される。

メリット:コンパイル時にフォーマット文字列をチェックしてくれるため、実行時のクラッシュを防げる。

C の printf は、フォーマット指定子(%d, %s 等)が間違っていても、実行時までエラーがわからない。
Rust の println! は、コンパイル時にチェックしてくれるので、型安全 だ。

マクロには ! をつける
これは Rust の文法規則で、マクロ呼び出しには必ず ! をつける。

  • println! - 標準出力に書き込む
  • vec! - ベクタを作成する
  • panic! - プログラムを異常終了させる

Vim、祈り駆動開発

さて、もう少し複雑なコードを書いてみる。

Vim で src/main.rs を開く。

fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = add(3, 5);
    println!("result = {}", result);
}

そういえば、Eclipse を激重だの、VSCode をお節介なおばさん呼ばわりしていた
過去の自分の言葉が脳裏をよぎる。

「VSCode なんて軟弱者が使うもの。私は Vim だ。Vim こそが正義だ。」

……何も見えない。

型も、補完も、エラーも、ドキュメントも。

全部、頭の中で覚えておくしかない。

コンパイルして、初めてエラーがわかる。

つまり、私の開発サイクルはこうなる。

  1. 書く
  2. 祈る
  3. ビルドする
  4. 怒られる
  5. 凹む
  6. 書く(最初に戻る)

効率が悪い。

HUNTER×HUNTER に出てくるネテロ会長の修業時代だって
もう少し効率的だったと思う。

依存が依存を呼ぶ

調べてみると、rust-analyzer というツールがあるらしい。

Rust のコードを解析して、

  • 補完
  • エラー表示
  • 定義ジャンプ
  • ドキュメント表示

などをやってくれるという。

救世主ではないか。

LSP という言葉もここで知る。

どうやら rust-analyzer は LSP サーバで、Vim 側にはそれと喋るための「仕組み」が必要らしい。

Vim に LSP を喋らせる

.vimrc に設定を書く。
LSP 用のプラグインを入れる。
Vim を再起動。

……動かない。

ログを見ると、見慣れないエラーメッセージが流れている。

[rust-analyzer] failed to parse JSON response

何これ。

調べる。

どうやら、入れた LSP プラグインには
さらに別の依存があるらしい。

  • Python が必要
  • Node.js が必要
  • あるいは C++ のビルドが必要

環境によって違う。

Node.js ?

それ、JavaScript の実行環境だよな?

なんで Vim で Rust を書くのに JavaScript が要るんだ。

依存は連鎖する

LSP プラグインを動かすために Node.js を入れる。
Node.js のバージョンが合わない。
別のプラグインが古い。
設定例が微妙に今と違う。

設定を直す。
Vim を再起動する。
まだ動かない。

数時間経過。

気付けば私は、

  • Vim の設定を読み
  • プラグインの README を眺め
  • エラーメッセージを検索し

Rust ではなく、Vim 環境を学んでいる。

私がやりたかったのは、Vim の設定ではない。
Rust の勉強だ。

おばさんに救われた日

仕方なく、Visual Studio Code を起動した。

Extensions から rust-analyzer をインストール。

公式の rust-analyzer(rust-lang.org) を選ぶ。
検索すると似た名前の拡張がいくつか出るので、青色のバッジ rust-lang.orgIdentifier: rust-lang.rust-analyzer を確認してから入れる。
おじさんはこういうところで一度痛い目を見る世代なので、先に言っておく。

私の場合は WSL 上で開発しているので、Install in WSL: Ubuntu でインストール。

数秒で完了。

src/main.rs を開く。

let x = 5;

x にカーソルを合わせると、ポップアップが表示された。

let x: i32

型情報が見えてる。

しかも、コードの横に薄っすらと : i32 というヒントが表示されている。
これが 型推論の可視化 だ。

println!! を消してみると、即座に赤い波線が表示される。

use the `!` to invoke the macro

リアルタイムでエラーが出てる。

セミコロンを忘れた瞬間に、画面が赤く染まる。

関数にカーソルを合わせると、シグネチャが表示される。
補完も効く。
ドキュメントも見れる。

全部、動いてる。

インストールから、ここまで 1分もかかっていない


【技術解説:rust-analyzer と LSP の役割】

rust-analyzer は Rust 向けの Language Server(LSP)で、補完・定義ジャンプ・型表示などをエディタに提供する。

LSP(Language Server Protocol)とは?

  • エディタと言語処理機能を分離するプロトコル
  • エディタ側:VSCode, Vim, Emacs 等
  • 言語処理側:rust-analyzer, TypeScript Language Server 等

これにより、一つの rust-analyzer が、あらゆるエディタで動く

Vim で rust-analyzer を動かすのが難しいのは、Vim 自体が LSP をネイティブサポートしていないからだ。プラグインを介して LSP と通信する必要があり、その設定が複雑になる。

VSCode は言語サーバと連携するための土台が整っているので、拡張機能を入れるだけで rust-analyzer が動きやすい。
逆に Vim/Neovim 側は「言語サーバと喋る係」を別途用意してやる必要があって、そこで人は Lua やら設定やらと格闘する。

Rust におけるエディタは、単なるテキスト入力機ではない。「コンパイラと対話するためのインターフェース」なのだ。

特におじさんエンジニアは、記憶力に頼らず、LSP に脳のメモリをオフロードすべきである。

(地味に効く)標準ライブラリ補完が弱いとき

補完が妙に弱い日があったら、rust-src を入れると効くことがあるらしい。

$ rustup component add rust-src

これで、標準ライブラリのソースコードがローカルに配置され、補完の精度が上がる。


私は、これまで VSCode を お節介なおばさん 呼ばわりしてきた。

「常に横から口出ししてくる」
「自分で考えてる感じがしない」

でも、違った。

おばさんは、優しかった。

Vim で数時間格闘して疲れ果てた私に、
「はい、お茶」と、何も言わずに差し出してくれる。

そんな存在だった。

私が環境構築で消耗している間、
VSCode は、すぐに始められる場所 を用意して待っていてくれた。

Vim は確かにストイックで美しい。
でも、Rust を学ぶには、適切な道具 が必要だ。

おばさん、ありがとう。

敗北ではなく、方針転換

Vim を諦めたわけではない。

ただ、今は Rust を学ぶフェーズ だ。

道具の美学は、Rust を理解してから取り戻せばいい。

  • Rust の学習フェーズでは、まず VSCode を使う
  • "書ける状態" を最優先する
  • Vim の美学は、あとで取り戻す(たぶん)

これは敗北ではなく、戦略的撤退 だ。

初学者向け:最短で Rust を始めるセットアップ

これから Rust を始める人のために、私が辿り着いた「最短ルート」を記しておく。

※2026年1月時点での実行ログ
※環境は Win11 WSL2

1. Rust のインストール

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Rust のツールチェーンマネージャーである rustup はシステム全体 (/usr/bin など) を汚さない。
ユーザーごとのホームディレクトリ内で完結するように設計されている ので安心。
sudo はつけずに通常ユーザーで実行すること。

インストールスクリプトが実行されると、Rustコンパイラ(rustc)とパッケージマネージャー(Cargo)の説明があり、以下の選択を求められる。

1) Proceed with standard installation (default - just press enter)
2) Customize installation
3) Cancel installation
>

学習を始めるにあたっては、ここでは標準構成 1 を選ぶ。
何も選択せずにそのまま Enter を叩くだけで良い。

インストール後、シェルを再起動するか、以下を実行:

$ source ~/.cargo/env

※環境は汚さないと書いたが、.bashrc. "$HOME/.cargo/env" と追加されるので次回ログインからは自動的にパスが通る

確認コマンド:

$ rustc --version
rustc 1.92.0 (ded5c06cf 2025-12-08)

$ cargo --version
cargo 1.92.0 (344c4567c 2025-10-21)

$ rustup --version
rustup 1.28.2 (e4f3ad6f8 2025-04-28)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.92.0 (ded5c06cf 2025-12-08)`

【ざっくり】

  • rustc はコンパイラそのもの
  • rustup は Rust 本体(ツールチェーン)の管理(Python の pyenv のようなもの)
  • cargo はパッケージ管理、ビルドツール実行(プロジェクトの作成・ビルド・実行・ライブラリ管理をすべてこなす)

よくある詰まり:コマンドが見つからない

  • ~/.cargo/bin が PATH に入っていない可能性
  • シェル設定ファイル(.bashrc, .zshrc 等)を確認
  • echo $PATH で PATH を確認し、~/.cargo/bin が含まれているか確認

2. VSCode のインストール

開発環境に入っていなければ、公式サイトから VSCode をダウンロードしてインストールする。

私のように WSL 上で開発を行うのであれば、WSL プラグインも入れておく。

3. rust-analyzer 拡張機能のインストール

VSCode を起動して:

  1. 左のサイドバーから Extensions(Ctrl+Shift+X / Cmd+Shift+X)を開く
  2. 検索欄に rust-analyzer と入力
  3. 公式の rust-analyzer(rust-lang.org) を選択
  4. Install をクリック

注意: 似た名前の拡張が出ることがあるので、発行元(rust-lang)だけ確認してから入れると安心。

4. プロジェクト作成

$ cargo new my_first_rust
$ cd my_first_rust
$ code .  # VSCode でプロジェクトを開く

これだけで、まず困らない Rust 開発環境が整う。

VSCode 内で View -> Terminal を開いて cargo run と打てば実行できる。

とりあえず現時点ではデバッガーなどは入れていない。

5. (オプション)標準ライブラリ補完を強化

補完の精度を上げたい場合:

$ rustup component add rust-src

トラブルシューティング

rust-analyzer が動かない

  1. VSCode を再起動
  2. Ctrl+Shift+P(Cmd+Shift+P)で Reload Window を実行
  3. rust-analyzer のログを確認:Output パネルで rust-analyzer を選択

コンパイルが遅い

初回ビルドは依存関係のダウンロードとコンパイルで時間がかかる。
2回目以降は高速になる。

Vim で使いたい場合(そんな人、私以外あんまりいないとは思うが)

Rust を理解してから挑戦することを推奨。
LSP 設定の参考:

  • Neovim: nvim-lspconfig プラグイン
  • Vim: vim-lsp プラグイン

ただし、設定は複雑で、Lua や VimScript の知識が必要。

  • Neovim は LSP 機能を内蔵しているので、Vim よりはルートが短そう(ただし Lua 沼はある)
  • まずは VSCode で Rust を学んでから、余裕が出たら Vim へ戻るのがコスパ良さそう(少なくとも私はそうする)

今回の学び

  • Rust のインストールは驚くほど簡単(rustup 最高)
  • cargo new でプロジェクトを初期化、cargo build でビルド、cargo run で実行
  • fn main() はデフォルトでユニット型 () を返す
  • Rust では式と文のシンタックスが違う
    • 式 (Expression) は セミコロンなし 値を評価し、その結果を次に渡す
    • 文 (Statement) は セミコロンあり その行で処理を完結させ、値を捨てる
  • println! はマクロなので ! をつける(コンパイル時型チェックのため)
  • Vim での環境構築は初心者には茨の道
  • VSCode + rust-analyzer は、初心者に優しい最強の組み合わせ
  • LSP はエディタと言語処理を分離する現代的な仕組み

Abe
「道具に固執するのは、老兵の悪癖。そこにある近代文明の恩恵を享受しよう。」


8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?