Charaken 技術系ブログ

技術系に関して学んだことや、気になったことを記事にしていく。

【Rust】がばがばRust独学 - 7. Packages/Crates/Modules

f:id:charaken:20191223210559p:plain

Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)

doc.rust-lang.org

今回は、Packages、Crates、Modulesについて学びつつ、気になった点をコード確認した結果を記事にしています。

  • versions
$ rustc --version
rustc 1.40.0 (73528e339 2019-12-16)


Packages、Crates、Modulesとは?

公式ドキュメント(Managing Growing Projects with Packages, Crates, and Modules - The Rust Programming Language)より

  • Packages:Cratesの構築・テスト・共有ができるCargoの機能
  • Crates:ライブラリや実行可能ファイルを生成するモジュールツリー
  • Modules:Pathsの構成・範囲・プライバシーの制御
  • Paths:構造体・関数・モジュールなどのアイテムへの命名方法

Packages and Crates

Cargo.toml ファイルにより、パッケージを提供可能です。

例えば、下記のように新しい my-project という名前のPackageを生成した場合、 Cargo.tomlsrc/main.rs が生成されます。

$ cargo new my-project

パッケージは1つ以上のBinary Crateと、0つ以上のLibrary Crateを含むものであり、各rootは以下のファイルにより管理されます。

  • src/main.rs:Binary Crate root
  • src/lib.rs:Library Crate root

Modules and Paths

下記により、新しい restaurant という名前のライブラリを生成することができます。

$ cargo new --lib restaurant

公式ドキュメント(Defining Modules to Control Scope and Privacy - The Rust Programming Language)より、コードを引用させていただくと、例えば、上記のライブラリの src/lib.rs に対して下記のように記載した場合、

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

下記のようなcrateからのpathにより、ライブラリが利用可能です。

crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

全体像

ドキュメントを読み進めていったが、わからん・・・ ということで、demo codeを作成しました。

github.com

training-rust/lib.rs at master · KentaHara/training-rust · GitHub を元に各記法について記載します。

各Moduleは mod により定義します。今回の場合、 TODOリストのCRUD(Deleteは省く)を意識して、 todo_list のModuleを作成しました。 内容としては、 commentdescription を扱えるような簡易的なものとなっています。

pub mod todo_list {
    // ...

    pub mod comment {
        // ...
    }

    pub mod description {
        // ...
    }
}

pub により、対象のModuleをpublicとして扱うことが可能になります。src/main.rc 内で自由に扱いたいため、Moduleは全てpublicにしています。

関数については一部記法の動作確認のために、description内で fn _get(id: u8) -> Option<String> を記述し、 pub fn get(id: u8) -> Option<String> からの読み出しをしています。

同一module内で対象の関数を呼び出す場合は、 self:: を利用します。

self::_get(id)

また、特定モジュールから見た親を利用する場合は super を利用します。今回の場合、comment から todo_listprint_messagecomment::create から呼び出しています。

super::print_message(&message);

src/lib.rs 内でModuleを利用する場合は、下記のように crate::todo_list::commenttodo_list::comment により利用することが可能です。

pub fn todo_list_comment_create(message: String) -> Option<u8> {
    crate::todo_list::comment::create(message)
}

pub fn todo_list_comment_update(message: String) -> Option<u8> {
    todo_list::comment::update(Some(1), message)
}

crate を利用することで絶対パスとなり、利用しない場合は相対パスによる呼び出しとなります。

デモコードには記載していませんが、 structやenumについてもModuleによる制御が可能です。

src/main.rstraining-rust/main.rs at master · KentaHara/training-rust · GitHub)で使用する場合には、下記のように use により、Moduleを呼び出します。

use pcm::todo_list::comment as ToDoComment;
use pcm::todo_list::description::*;
use pcm::todo_list_comment_create;
use pcm::todo_list_comment_update;

use pcm::todo_list::comment as ToDoComment;as を利用して対象のModuleを別名として利用することが可能です。 as を利用しないパターン、例えば use pcm::todo_list::comment ですと、comment::xxx により、処理で利用できます。

use pcm::todo_list::description::*;* は対象モジュール内の関数等を全て呼び出して利用可能とします。そのため、下記のような後続処理として利用可能です。

update(Some(1), String::from("description message"));

上記の場合は、関数名の衝突がありえます。例えば、 src/main.rs に対して、下記を追加した場合、main.rs が優先されます。

fn get(id: u8) -> Option<String> {
    match id {
        1 => Some(String::from("No.1 local Message")),
        _ => None,
    }
}

また、 src/lib.rs 直下の関数を利用する場合は、 use pcm::todo_list_comment_update; のように記述することで、利用可能です。

デモコードには記載はしておりませんが、公式ドキュメント(Bringing Paths Into Scope with the use Keyword - The Rust Programming Language)より、公開されているpackagesを利用する場合、 Cargo.toml に対して下記のように記述することで、利用可能です。

[dependencies]
rand = "0.5.5"

最後に

概念はドキュメントから分かるものの、実際の落とし込みは書いてみないとなんとも難しいですね・・・

Moduleを跨いだ場合の使用方法やDIに関しても今後知見を広めていければと考えております。

次は、Collectionsを記載していきます。


教えて、偉い人!

VSCodeuse にエラーが多発して、ジャンプができないため追っていくのが大変です・・・

f:id:charaken:20200101120100p:plain