目的
社内にRustを広めるために作った入門講座なのですが、せっかくなので記事として外部公開しようかなと思います。
学習する順番や情報量を操作しているので、他よりも学習しやすいかもしれないです(そうあったらいいなと思って作っています)。
前提
- C#やJavaなど、何らかの高級言語を触ったことがあることが前提です。というより、プログラミング言語初体験がRustはハード過ぎるのでオススメしません。
- Rustではメモリ管理の考えを理解することが重要です。なので、スタックメモリとヒープメモリの関係性は理解していることも前提としています。
これから学習したいという方はこちらの記事などがわかりやすいかと思います。
この前提を満たしている方のみ、以下から一緒に学習していきましょう!
Rust誕生秘話
Rustは元々エレベーターの故障から生まれました。
頻繁に故障するマンションエレベーターに悩まされていた開発者のグレイドン・ホアレは、制御ソフトウェアがC/C++によって書かれていることを知っていました。
C/C++は非常に速く動作するプログラムを作成でき、サイズもコンパクトに収まる言語です。
しかしながら、メモリ管理がプログラマーに完全に委ねられていることでクラッシュしやすいという危うい性質も持っています(そして残念ながら人間は完璧ではないのです)。
そこでグレイドン・ホアレは、誰が書いてもメモリバグを起こさない、非常に丈夫なさび菌のようなプログラミング言語を開発することにしました。
それがRust(さびを意味します)です。
Rustの特徴
RustはC/C++と同等の実行速度を持ちながら、所有権と呼ばれる仕組みを用いることで、ガベージコレクションなしでメモリ管理を実現しています。また、コンパイル時にメモリ安全をチェックする仕組みがあるため、基本的にnull参照・データ競合などが発生しません。
これがRustの最大の特徴です。
その他の特徴も含めて端的にまとめると下記のようになります。
- 高い安全性:
所有権システムによりメモリ安全をコンパイル時に保証する - 高パフォーマンス:
C/C++並みの実行速度を持ち、ランタイムやガーベジコレクションがない - 並列処理の安全性:
コンパイル時にスレッド間のデータ競合を防ぐ仕組みを持つ - 安易な例外処理を嫌う設計思想:
Rustにはtry-catchによる例外処理機構がない。Result型によりそれが失敗しうる処理なのかどうかを明示する必要がある - 統一されたパッケージ・ビルド管理:
Cargoという公式パッケージ管理ツール兼ビルドシステムにより、開発に必要な一連の作業を共通のコマンドで実行可能 - 豊富なドキュメンテーション機能:
ソースコードのコメントから自動的にHTMLドキュメントを生成する機能を言語レベルで提供、コメント内のコードテストも可能
さあ、Rustの世界を冒険してみましょう!
インストール
rustup
というツールを使ってRustをインストールしましょう。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
アップデート確認ができればインストール成功です。
rustup check #Rust関連ツールのトータルアップデートチェック
rustc --version #Rust自体のバージョン確認
インストールに伴って下記コマンドが利用できるようになります。
-
rustup
:Rustのインストール・バージョン管理 -
rustc
:Rustのコンパイラ -
cargo
:Rustのプロジェクト・依存関係管理
今後バージョン更新等行いたい場合、下記コマンドで更新しましょう。
rustup update
プロジェクトを作ってみよう
cargo
コマンドでプロジェクトを作成しましょう。
cargo new [project_name]
お好みのコードエディタでプロジェクトフォルダを開いてください。
VSCode
が拡張機能の観点で安牌ですが、Rust製コードエディタのZed
も親和性高くてオススメです。
code [project_name] #VSCodeの場合
zed [project_name] #Zedの場合
Cargo.toml
を確認してみよう
生成されたファイル群の中にCargo.toml
というファイルがあります。
これはパッケージ情報、依存関係などプロジェクトに関連する情報を定義するファイルです。
[package]
name = "testes"
version = "0.1.0"
edition = "2024"
[dependencies]
他のプログラミング言語であまり見ない項目としてedition
がありますね。
これはRustが破壊的変更を許容するために生み出された仕組みです。
Rustは同一エディション内では破壊的変更を行わないことが約束されています。これにより、バージョンアップによる想定外の動作不良を回避することができます。
ただし、Rustという言語をより洗練させていくために、どうしても破壊的変更が必要になる場合があります。この場合、バージョンではなくエディションの方が更新される仕組みとなっています。
現在の最新エディションは2024
です。
これから使い始める人は、最新エディションである2024
一択で問題ないです。
Hello, world!
今度はmain.rs
を見てみましょう。
拡張子rs
はRust言語のソースコードを意味します。
fn main() {
println!("Hello, world!");
}
お馴染みのHello, world!
が記述されていますね。
試しに実行してみましょう。
ターミナルウインドウを開いて、下記コマンド実行でプログラムをデバッグ実行することができます。
cargo run
無事、Hello, world!
がターミナルに出力されれば成功です。
Rustの関数定義
基本
main
はRustにおけるエントリーポイントです。
Rustでは関数宣言はfn
によって行われます。
fn [function_name](argument: Type) -> ReturnType {
...処理...
}
試しに新しい関数を実装してみましょう。
fn greet() -> String {
let greeting = format!("Hello, {}!", "Alice");
println!("{}", greeting);
greeting
}
Rustにおける戻り値の記述ルール
他のプログラミング言語と異なる点として、Rustには戻り値を返す表現方法が2つあります。
ひとつは一般的なプログラミング言語でよく見るreturn
を付与する方法です。
fn greet() -> String {
let greeting = format!("Hello, {}!", "Alice");
println!("{}", greeting);
return greeting;
}
もうひとつは末尾のセミコロンを省略する記法です。
先程の関数を改めて見ると、最後の行にセミコロンがなく、戻り値の表現になっていることが分かるかと思います。
fn greet() -> String {
let greeting = format!("Hello, {}!", "Alice");
println!("{}", greeting);
greeting
}
Rustでは一般的にこのセミコロン省略記法が採用されています。
普段別の言語に慣れている人からするとreturn
の方が慣れ親しんでいるしわかりやすくて良い、と思うかもしれません。
が、これにはきちんと意図があります。
Rustでは戻り値の記法を、目的に合わせて変えることが一般的になっています。
-
return
記法:
処理の途中で抜ける場合でのみ使用 - セミコロン省略記法:
処理が最後まで完遂した場合でのみ使用
これにより、途中抜けパターンはreturn
を目印に、最後まで処理が進んだ場合は処理の末尾を確認することで、戻り値の位置付けやパターンを判別しやすくしています。
慣れるまではついついreturn
と書きたくなってしまいますが、意識して使い分けていきましょう。
戻り値を受け取って処理実行
先程の関数を利用するよう、main
関数を修正しましょう。
fn main() {
let greeting = greet();
println!("{}!!", greeting);
}
Rustは静的型付け言語ですが、昨今の標準機能である型推論を持っているので、変数宣言で型表記は原則不要です。
コマンド実行して問題なく動作することを確認しましょう。
cargo run
下記結果になれば成功です。
Hello, Alice!
Hello, Alice!!!
Rustの変数束縛
Rustの変数は基本的に不変(アドレス参照を含め、値を変更できない)です。
fn main() {
let a = 5;
println!("{}", a);
a = 10; // これはコンパイル時点でエラーになります
println!("{}", a);
}
値を変更したい場合、mut
をつけて明示的に可変を表す必要があります。
fn main() {
let mut a = 5;
println!("{}", a);
a = 10; // これはOK
println!("{}", a);
}
Rustの制御構文
if式
Rustでは、if
は文ではなく式です。そのため、値を返すことができます。
さらに他の言語と異なる点としては、()
が不要という点が挙げられます。
fn main() {
let a = 5;
let b = if a > 0 {
"a is positive"
} else {
"a is non-positive"
};
println!("{}", b);
}
while
こちらもif
と異なり、文になります。値を返すことはできません。
他の言語と異なる点としては、()
が不要という点が挙げられます。
fn main() {
let mut count = 0;
let mut a = 0;
while count < 10 {
a += 5;
count += 1;
}
println!("{}", a);
}
お疲れ様でした!
お疲れ様でした。初Rust体験、いかがだったでしょうか?
次回は、基本的な型表現・トレイト・ジェネリクスについて学びます。
Rust入門講座②基本的な型・トレイト・ジェネリクス
Hope you enjoy it!