Rust学習 - モジュールの概念に入門(+α:cargo基本お作法を点検)
タイトルの通りもくもくと頑張る。画像は夜の空のイメージ。秋を先取りや。それにしてももうこんな時間。
本題
モジュールの概念に入門する。
ソースファイル作って中に関数掛けばいいんでしょ? super, self, use, modなんとなくわかる 定義を確認していきましょう。
モジュールとは
コードを階層的に分割し、お互いの機能を隠蔽・公開する仕組み。モジュールシステム。
Rustにはコードを階層的に分割し、お互いの機能を隠蔽・公開するための強力なモジュールシステムが存在します
モジュールは要素の集合です。
要素とは
関数(fn)
構造体(struct)
トレイト(trait)
implブロック(impl)
さらには他のモジュール
など。
モジュールは「mod」キーワードを使って定義する
関数「hello」を要素に持つ「my_mod」という名前のモジュールを実装した場合、以下のようなコードになる
mod my_mod {
fn hello() {
println!("Hello")
}
}
モジュールの特徴
モジュールの特徴を理解していく。
10.1. プライベートとパブリック
デフォルトでは、モジュール内の要素はプライベート
モジュールはファイル・ディレクトリ間の階層構造と対応関係にある
モジュールにお互いがどのように見えているか
これは、なんとなくは理解できる。
モジュールに定義された関数などを呼び出す側の視点で考える
モジュールに定義された関数などを呼び出す側で「mod my;」というコードを書いた場合
これは、「my.rs」という名前のファイル、もしくは、「my/mod.rs」 という名のファイルを探し、そのファイル内に実装されているモジュールの定義を、呼び出し側では「my」という名前で使用することができる。とかように解釈されるらしい。
「self」
selfキーワードは現在のモジュールスコープを示す。
これはなんとなくわかる。つまりこういうことだ。
mod my {
fn brother_function() {
println!("called `brother_function()`");
}
struct Candy {
price: u32,
flavor: String,
size: u8,
}
impl Candy {
pub fn new(price: u32, falvor: String, size:u8) -> Self {
Candy {
price,
flavor,
size,
}
}
}
pub fn hello() {
// `super`は親スコープ(`my`の外側)を参照する。
self::brother_function();
let orange = self::Candy::new(10000, "orange", 1)
}
}
「super」
「super」とは、「my」モジュールの外側、親スコープを参照することを意味する。
親スコープ。。?
今度はモジュール定義する側の視点で考える
仮に「my」というモジュールを定義して、その親スコープに定義された「parent_function」という関数を参照するケースを考えると、以下のようなコードになる。
fn parent_function() {
println!("called `parent_function()`");
}
mod my {
pub fn hello() {
// `super`は親スコープ(`my`の外側)を参照する。
super::parent_function();
}
}
use宣言「要素の絶対パスを新しい名前にバインドする」という意味を持つらしい。
説明はいまいちピンとこないが、定義済みのモジュールを呼びやすくする仕組みと理解した。
説明によれば、「use super::*;」と宣言すると、親スコープの関数をsuper::の記載なしに呼び出せるらしい。
つまり、以下のような2つのモジュールが同じフォルダにあった時
「you」モジュールの関数を使用することをmain関数を定義したモジュール内で宣言し、必要に応じて、as句で名前をつけるという使い方をする。ここでは「」
src/you.rs
mod you {
pub fn greeting(who: &str) {
println!("Hi {}", *str);
}
}
src/main.rs
use you::greeting as hi_bob;
fn main() {
// `you::greeting` 関数の定義が実行される
let bob: String = "Bob";
hi_bob(bob);
}
以上。モジュールについてなんとなく理解した。
余談(命名について)
RFC0430という標準規約がある。モジュールの名前はスネークケースで決められている。
ちなみに、関数の名前もスネークケース、構造体などの型の名前は、アッパーキャメルケースで記述、するお作法らしい
github上の以下のマークダウンを参照した。
本題+α
cargoの使い方と定義を確認する時間だ。
ドキュメントざっくり読んだが、しっくり来る説明がない。コマンドを勘で実行してる。実際の挙動は見てなんとなくわかる。定義がどのようになっているのかを知りたい。
概念を理解し、それから実践していく。
cargo概念理解
CargoはRustのビルドシステム兼パッケージマネージャ
以下の2つのドキュメントを熟読したところ、概念を理解することができた。結構わかりにくいな。
cargo
依存関係の管理
crates.io(Rustの公式パッケージレジストリ)とのインテグレーション
バイナリを作る
プロジェクトをビルドする/cargo build/全ての依存関係の解決/必要なクレートのダウンロード/自分のクレートを含む全てのビルド
プロジェクトをビルドして更に実行する/cargo run
バイナリクレートをローカルにインストール/cargo install/バイナリ=たぶん実行形式ファイルのこと。bin/クレート=ライブラリに相当する概念
Rustのデフォルトパッケージレジストリ(Rust community's central package registry)
cargo実践確認(バイナリを作る)
バージョン確認
$ asdf current rust
rust 1.62.1 /Users/e_fujikawa/.tool-versions
$ cargo --version
cargo 1.62.1 (a748cf5a3 2022-06-08)
パッケージビルド
$ cargo build --release
Compiling libc v0.2.126
Compiling hex v0.3.2
Compiling commoncrypto-sys v0.2.0
Compiling commoncrypto v0.2.0
Compiling crypto-hash v0.3.4
Compiling blockchain-in-rust v0.1.0
Finished release [optimized] target(s) in 2.60s
cargo の挙動、基本的なことを知りたいだけなのに、公式のドキュメント深く全部読まないとわからない。必要な情報が出てこない。
「プロジェクトをビルドする/cargo build/全ての依存関係の解決/必要なクレートのダウンロード/自分のクレートを含む全てのビルド」で理解できた。
またあした!