rustでプロジェクトを管理する方法【パッケージ、クレート、モジュール】

rust

この記事で書くこと

rustのプロジェクトを管理する際に使われる概念について、学んだ内容をまとめました。以下のことがわかるように書きます。

  • パッケージとは何か
  • クレートとは何か
  • モジュールとは何か

パッケージ、クレート、モジュールを使ったプロジェクト管理

パッケージ、クレート、モジュールの関係を図にしてみました。

パッケージは一つのディレクトリです。この中にはクレートと呼ばれるファイル(or ディレクトリ)が入っています。クレートには2種類あります。lib.rsがrootの「ライブラリクレート」とmain関数を含むファイル(上記の図ではbinと表記)がrootの「バイナリクレート」です。「ライブラリクレート」については、パッケージ内に1つまで(0でも良い)という制限があります。「バイナリクレート」はいくつでも良いです。各クレートは関連のある関数や構造体をひとまとまりとして、モジュールを作ることができます。これにより、小さく分割してプロジェクトを管理することができます。

パッケージの作り方

ライブラリクレートを含むパッケージを作成する場合

$ cargo new adder --lib

この場合、以下のようなパッケージができます。

$ tree adder
adder/
├── Cargo.toml
└── src
    └── lib.rs

バイナリクレートを含むパッケージを作成する場合

cargo new add-one

この場合、以下のようなパッケージができます。

$ tree add-one/
add-one/
├── Cargo.toml
└── src
    └── main.rs
ライブラリクレートとバイナリクレートを含むパッケージの作り方

両方のクレートを含むパッケージの構造は以下のようになります。

$ tree adder
adder/
├── Cargo.toml
└── src
    ├── lib.rs
    └── main.rs

このような構造にするには、2通りの方法があります。といっても、cargoでパッケージを作って、手動で足りない方のファイルを追加するだけです。

$ cargo new adder --lib
$ touch adder/src/main.rs
$ cargo new adder
$ touch adder/src/lib.rs
ライブラリクレートと複数のバイナリクレートを含むパッケージの作り方

基本的には、「ライブラリクレートとバイナリクレートを含むパッケージの作り方」と同じですが、バイナリクレートを置く場所はbin以下になります。この中であれば、いくつでもバイナリクレートを置くことができます。

$ tree adder
adder/
├── Cargo.toml
└── src
    ├── bin
    │   ├── hello.rs
    │   └── hello_world.rs
    └── lib.rs

モジュールへの分割

それぞれのクレートはモジュールへと分割することができます。上記の「ライブラリクレートと複数のバイナリクレートを含むパッケージ」のライブラリクレートをモジュールに分割してみます。

例えばadderのlib.rsが以下のようなライブラリクレートであったとします。

pub fn add_two(t: usize) -> usize {
    t + 2
}
pub fn add_three(t: usize) -> usize {
    t + 3
}

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

足し算を提供するクレートですが、「2つの引数を足す」関数と「ある数に定数を足す」関数で機能を分けることができます。ある数に定数を足す」関数をモジュールにしてみると以下のようになります。

mod add_something {
    pub fn add_two(t: usize) -> usize {
        t + 2
    }
    pub fn add_three(t: usize) -> usize {
        t + 3
    }
}
pub fn add(left: usize, right: usize) -> usize {
    left + right
}

add_somethingモジュールを使いたいときは、useキーワードを使います。

mod add_something {
    pub fn add_two(t: usize) -> usize {
        t + 2
    }
    pub fn add_three(t: usize) -> usize {
        t + 3
    }
}

use add_something::*;
fn use_add_something(){
    add_two(4);
    add_three(4);
}

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

モジュールをファイルへ切り出す

モジュールは別ファイルへ書くことができます。上記の例を別ファイルに切り出すとき、以下の2通りの構成が可能です。

$ tree adder
adder/
├── Cargo.toml
└── src
    ├── add_something.rs
    ├── bin
    │   ├── hello.rs
    │   └── hello_world.rs
    └── lib.rs
$ tree adder
adder/
├── Cargo.toml
└── src
    ├── add_something
    │   └── mod.rs
    ├── bin
    │   ├── hello.rs
    │   └── hello_world.rs
    └── lib.rs

このとき、lib.rsは以下のようになります。

mod add_something;

use add_something::*;
fn use_add_something(){
    add_two(4);
    add_three(4);
}

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}

add_something.rsまたはadd_something/mod.rsは以下のようになります。

pub fn add_two(t: usize) -> usize {
    t + 2
}
pub fn add_three(t: usize) -> usize {
    t + 3
}

元々あった”mod add_something{}”の中身を別ファイルに移動して、”mod add_something;”にしました。rustではモジュールの構造はファイル配置の構造と一致するようになっています。

ここで使用した例について

ここで使用した例については、こちら(github)に上げています。どうぞご自由にお使いください。

コメント

タイトルとURLをコピーしました