Rust超初心者向けチュートリアル, ツールとマクロの紹介
社内向けに発表した資料を多少改変してコミュニティに還元します.
初心者向けガイドなので既に書いてるって人は見なくても良いと思います.
実際に手を動かしてツールをインストールしてもらうことを推奨します.
適当に動かしたい人向け
webサーバがRustコードを実行してくれます.
Rustのダウンロード数100位までのライブラリが使えるので, 大抵のサンプルはインストール不要でここで実行できます.
shareしたコードは自動的にgistにアップロードされるのでそこだけは注意.
share機能でパーマリンクで簡単にコードをシェアできるので, TwitterやGitHubやChatworkでソースを送り合うのに便利です.
rustup
rustup.rs - The Rust toolchain installer
curl https://sh.rustup.rs -sSf | sh
システムの方法で入れたいとかはnightlyの扱いが地獄なので諦めてrustupを使った方が良いです.
stableかnightlyか
nightlyは不安定版なので一般ユーザがnightlyの機能を利用するべきではありません.
しかしASTなど内部構造を触れるAPIは不安定なので, Rustの開発ツールはよくnightlyで開発されています.
どうせnightlyが必要になるのでnightlyを選択して, 業務コードではnightlyの機能使わないのもありだと思います. 容量削減にもなります.
nightlyの機能を使うにはfeature
マクロをソースコードに書く必要があるため,
うっかりnightlyの機能を使ってしまうということもないでしょう.
追記: nightly上で安定化された場合などにうっかり使ってしまう可能性があるそうです… stableで問題ない人はそちらの方が良いでしょう.
後からrustup default stable
のように簡単に変更できます.
パスの追加
パスは.profile
に追記されるのでそのへん気にしてない人はそのままで追加されます.
自分はパラノイアなので自分で
~/.profile
に
export PATH=$PATH:~/.cargo/bin
って書いてます.
source ~/.cargo/env
でも良さそうです.
.zshrc
とかに書くとシェル以外のシステムがrustc
を認識できずにうまくいきません.
cargo
とかrustup
のシェル補完を有効にしたいときは,
zshなら以下で出来ます.
bashでも似たようなことが出来るはずです.
mkdir -p /tmp/$USER-zsh-completions/
if hash rustup 2>/dev/null; then
rustup completions zsh > /tmp/$USER-zsh-completions/_rustup
fi
fpath=(
/tmp/$USER-zsh-completions/
$fpath)
if hash rustc 2>/dev/null; then
fpath=($(rustc --print sysroot)/share/zsh/site-functions $fpath)
fi
たまにrustupでコンポーネントがインストールできなくなる時があります
error: component 'clippy' for target 'x86_64-unknown-linux-gnu' is unavailable for download
Rustup packages availability on x86_64-unknown-linux-gnu
などを見て上流がバグってるか確かめましょう.
バグっていたら,
rustup install nightly-2019-02-08
のように正常なツールチェインをインストールしましょう.
参考: https://rust-jp.slack.com/archives/C8FLSR5F1/p1550128316010700
rustfmt
rust-lang/rustfmt: Format Rust code
rustup component add rustfmt
Goで言うgofmt, C++で言うclang-formatに相当します.
チーム開発するなら絶対に入れて下さい.
ファイル保存時に動くようにしてください. Emacsだとrustic-modeというのが自動で設定してくれます. Gentoo上のEmacsでまともなRust環境を構築しました, バグ報告で問題が解決しました - ncaq
clippy
rust-lang/rust-clippy: A bunch of lints to catch common mistakes and improve your Rust code
rustup component add clippy
コンパイラ警告がカバーしない範囲の警告を出してくれます.
let foo_regex = Regex::new("foo").unwrap();
みたいなの書いたら「それ正規表現使わずにString::contains
でええやろ」
と警告を出してくれます.
rls
rustup component add rls rust-analysis rust-src
LSP(Language Server Protocol)についてはご存知ですか?
これまでは言語(処理系)とテキストエディタの組み合わせ全てに, リアルタイムエラーチェックや補完機構を実装する必要がありました.
例えばRust, TypeScript, Scala, PHP, C#の5言語を, Emacs, Vim, VSCode, Atomの4テキストエディタに対応させようとすると, 作る必要のあるプロダクトは5*4=20ですね.
LSPはこの問題を解決して, 言語にLSPバックエンドを実装して, テキストエディタにLSPフロントエンドを実装して, LSPで通信することで組み合わせの爆発を防ぎます.
さっきの例だと実装数は5+4=9で済みます.
rlsはRustのLSPバックエンドです.
よく使う機能として, リアルタイムエラーチェック, 補完, フォーマット, 警告に対応したコード自動修正などがあります.
EmacsのLSPフロントエンドはlsp-modeとeglotが有名で, 私はシンプルなeglotを使っています.
rustupのコンポーネントのインストールを毎回するのが面倒くさい問題の解決
私はこういうシェルスクリプトを書いて実行してます.
#!/usr/bin/env zsh
world="
clippy
rls
rust-analysis
rust-src
rustfmt
"
echo $world|xargs rustup component add
cargo watch
passcod/cargo-watch: 🔭🚢 Watches over your Cargo project's source
インストール方法
cargo install cargo-watch
使い方
cargo watch -x check
でファイルに変更がある度にシェル上でチェックが走ります.
cargo edit
killercup/cargo-edit: A utility for managing cargo dependencies from the command line.
cargo install cargo-edit
Cargo.tomlのadd
やupdate
をコマンドラインで行なえます.
マクロ
マクロ https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/macros.html
よく紹介されていて多用されているマクロを紹介します.
panic
プログラム(スレッド)がどうしようもない状態に陥ってクラッシュさせるしかない時に使いましょう.
try
今は?
演算子が安定化したので使う必要はありません.
後で述べます.
unreachable
論理的に絶対この分岐に行かないはずだと思う場所に設置しましょう.
panicより短い.
unimplemented
型チェックをごまかしてとりあえずコンパイル通したい時に置いて後で実装しましょう.
TODOとか書くより検索性に優れています.
dbg
dbg!(error);
は
eprintln!("error: {}", error);
ほぼ等価として扱えます.
更にdbg!
は値を返すのでdbg!
にdbg!
を埋め込んで内部でトレースすることも出来ます!
Haskellのtrace
と似たような使い方が出来るというわけですね.
env_loggerとは併用出来ないのでプリントデバッグ専用に使いましょう.
?演算子
use std::fs::File;
use std::io::prelude::*;
fn foo1() -> std::io::Result<String> {
match File::open("foo.txt") {
Err(err) => return Err(err),
Ok(mut file) => {
let mut contents = String::new();
match file.read_to_string(&mut contents) {
Err(err) => return Err(err),
Ok(_) => {
assert_eq!(contents, "Hello, world!");
Ok(contents)
}
}
}
}
}
use std::fs::File;
use std::io::prelude::*;
fn foo2() -> std::io::Result<String> {
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
Ok(contents)
}
まともになりました.
ちなみに現実的にはファイルを読み込んで文字列にする関数は std::fs::read_to_string - Rust として標準ライブラリにあります.
Resultの関数にOption返すメソッドやResultのエラーの型が違うメソッドが混ざった場合.
- Option:
ok_or
を使って何かErr
を返す - Result:
or
を使って同じ型のErr
を返すかmap_err
で型を変換する
が最適解のようですね.
質疑応答: 正規表現の例とかでunwrap
って避けられないの?
マッチとかの状況ではmatch
やif let
を使ってきちんとマッチしたかどうか確認はする必要はあります.
正規表現ビルドでunwrap
しないといけないのはどうせ起動時だけなので失敗しても大した問題は無いので,
「自分はコンパイラより賢いんだ」という自信を持ってunwrap
しても良いと思います.
実際まだまだコンパイラは人間より賢くないので,
人間にとって自明に失敗しないことがわかる場合もRustはResult
を使いたがることがよくあります.
また他の言語は常にunwrap
しているようなもので,
unwrap
してもちゃんと失敗した場所が通知されるので,
他の言語を使うよりは安全だと思います.
追記(2019-02-28T16:29:30+09:00) rlsとdbgの使い方について
blackenedgoldさんから指摘を受けて記事を修正しました.
追記(2019-03-01T16:39:37+09:00) nightlyをデフォルトにしないことを推奨することについて
nightlyだと安定化された機能をうっかりfeature無しで使ってしまう可能性があるのでは https://rust-jp.slack.com/archives/C1EV1LGHH/p1551342507002100
という指摘がlo48576さん, tatsuya6502から入りました.
プロジェクトで使用するRustツールチェインのバージョンをチームで共有する - Qiita を使って必要なときだけnightlyを使えることは知っていました.
しかしEmacsのrustic-modeがclippy要求などをnightly要求をしてきたので, nightlyデフォルトの方が面倒が無いのでnightlyをデフォルトした方が楽そうだなと判断してしまいました.
今ではrustic-modeもstableで動くようです.
stableでも動く人はそれでも良いでしょう.
追記(2019-03-08T21:12:48+09:00)
CIでstableでビルドとテストが通ることを確認していれば, nightlyの機能をうっかり使ってしまうことは無いのではと思えてきました.
しかしCIでstableを使う以上ローカル開発環境もやはりstableに揃えた方が良いですね…
nightlyはもはやrustic-modeにおいても不要になってきたので, もうnightlyにする必要はやはり無いのかもしれません.