さび4 -モジュール、木枠、テスト、ドキュメンテーション


さび4 -モジュール、木枠、テスト、ドキュメンテーション


モジュール


モジュールツリーをRustで明示的に構築する必要があります.モジュールツリーへのファイルシステムツリー間の暗黙のマッピングはありません.
以下のモジュール構造を作成したいと思います(この木の葉は関数です)
crate
 └── front_of_house
     ├── hosting
        ├── add_to_waitlist
        └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment
このモジュール構造を作成するには、次のように追加できますmain.rs ファイル.
// main.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() {}
    }
}

fn main() {}
または、次のファイル構造を作成します.
src
 ├── front_of_house
    ├── mod.rs
    ├── hosting.rs
    └── serving.rs
 └── main.rs
The hosting.rs and serving.rs ファイルはモジュールで、上記の機能を含んでいます.mod.rs ファイルは以下のようなサブモジュールを宣言しなければなりません:
// mod.rs
mod hosting;
mod serving;
インmain.rs , また、宣言する必要がありますfront_of_house サブモジュール.
// main.rs
mod front_of_house;

fn main() {} 
呼ぶことができるfront_of_house::serving::take_order(); 機能からmain.rs , この関数はpublicでなければなりません.また、その関数につながるモジュールはパブリックでなければなりません.
機能を公開します.
// serving.rs
pub fn take_order(){}

fn serve_order(){}

fn take_payment(){}
メイクするserving モジュール mod.rs パブリック.
// mod.rs
mod hosting;
pub mod serving;
今から我々はそれを呼び出すことができますmain() .
mod front_of_house;

fn main() {
    // Absolute path
    crate::front_of_house::serving::take_order();

    // Relative path
    front_of_house::serving::take_order();
}
モジュールの詳細な説明については、次の記事を読んでください.
Clear explanation of Rust's module system
Rust modules explained

公共建築物


mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }

    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}

pub fn eat_at_restaurant() {
    // Order a breakfast in the summer with Rye toast
    let mut meal = back_of_house::Breakfast::summer("Rye");
    // Change our mind about what bread we'd like
    meal.toast = String::from("Wheat");
    println!("I'd like {} toast please", meal.toast);

    // The next line won't compile if we uncomment it; we're not allowed
    // to see or modify the seasonal fruit that comes with the meal
    // meal.seasonal_fruit = String::from("blueberries");
}

公共施設


mod back_of_house {
    pub enum Appetizer {
        Soup,
        Salad,
    }
}

pub fn eat_at_restaurant() {
    let order1 = back_of_house::Appetizer::Soup;
    let order2 = back_of_house::Appetizer::Salad;
}

相対パスと絶対パスでの使用


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

pub fn eat_at_restaurant_rel() {
        // relative
        use self::front_of_house::hosting;
    hosting::add_to_waitlist();
}

pub fn eat_at_restaurant_abs() {
        // absolute 
    use crate::front_of_house::hosting;
    hosting::add_to_waitlist();
}

pub fn eat_at_restaurant_full() {
        // full path
        use crate::front_of_house::hosting::add_to_waitlist;
    add_to_waitlist();
}

複数モジュールの使用


use std::io::{self, Write};
// Write does not need to be prefixed but you’d still need to do io::BufReader and so on.
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}

        pub fn seat_at_table() {}
    }
}

fn eat_at_restaurant() {
    use front_of_house::hosting::{self, add_to_waitlist};
    hosting::seat_at_table();
    hosting::add_to_waitlist();
    add_to_waitlist();
}

glob演算子


use std::collections::*;

スーパーと自己


fn function() {
    println!("called `function()`");
}

mod cool {
    pub fn function() {
        println!("called `cool::function()`");
    }
}

mod my {
    fn function() {
        println!("called `my::function()`");
    }

    mod cool {
        pub fn function() {
            println!("called `my::cool::function()`");
        }
    }

    pub fn indirect_call() {
        // Let's access all the functions named `function` from this scope!
        print!("called `my::indirect_call()`, that\n");

        // The `self` keyword refers to the current module scope - in this case `my`.
        // Calling `self::function()` and calling `function()` directly both give
        // the same result, because they refer to the same function.
        self::function();
        function();

        // We can also use `self` to access another module inside `my`:
        self::cool::function();

        // The `super` keyword refers to the parent scope (outside the `my` module).
        super::function();

        // This will bind to the `cool::function` in the *crate* scope.
        // In this case the crate scope is the outermost scope.
        {
            use crate::cool::function as root_function;
            root_function();
        }
    }
}

fn main() {
    my::indirect_call();
    // prints:
    // called `my::indirect_call()`, that
    // called `my::function()`
    // called `my::function()`
    // called `my::cool::function()`
    // called `function()`
    // called `cool::function()`
}

依存


木枠についてio


// Cargo.toml
// ...
[dependencies]
time = "0.2.16"
// main.rs
use time;

fn main() {
    println!("2020 has {} days", time::days_in_year(2020));
}

ローカル


projects
 ├── hello_utils
    └── src
        └── lib.rs
 └── my_project
     └── src
         └── main.rs

// projects/my_project/Cargo.toml
// ...
[dependencies]
hello_utils = { path = "../hello_utils" }
// lib.rs
pub fn hello(){
    println!("Hello!");
}
// main.rs
use hello_utils;

fn main() {
    hello_utils::hello();
}

ワークスペース


クリエイトadd フォルダ.
内部add フォルダを実行cargo new adder and cargo new add-one --lib .
これは次の構造体を作成します.
add
 ├── Cargo.lock
 ├── Cargo.toml
 ├── add-one
    ├── Cargo.toml
    └── src
        └── lib.rs
 ├── adder
    ├── Cargo.toml
    └── src
        └── main.rs
 └── target
// add/Cargo.toml (note that [package] section is missing)
[workspace]

members = [
    "adder",
    "add-one",
]
// add/add-one/src/lib.rs
pub fn add_one(x: i32) -> i32 {
    x + 1
}
// add/adder/src/main.rs
use add_one;

fn main() {
    let num = 10;
    println!(
        "Hello, world! {} plus one is {}!",
        num,
        add_one::add_one(num)
    );
}
// add/adder/Cargo.toml
// ...
[dependencies]
add-one = { path = "../add-one" }
走るcargo run -p adder .
出力:Hello, world! 10 plus one is 11!.

テスト


私たちは同じファイルか別々にテストを書くことができます.

単体テスト


Put unit tests in the src directory in each file with the code that they’re testing. The convention is to create a module named tests in each file to contain the test functions and to annotate the module with #[cfg(test)].


// src/lib.rs
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(4, 2 + 2);
    }
}

統合テスト


Integration test use your library in the same way any other code would, which means they can only call functions that are part of your library’s public API. To create integration tests, you need a tests directory next to your src directory.


// tests/integration_test.rs
use adder;

#[test]
fn it_adds_two() {
    assert_eq!(4, adder::add_two(2));
}
次のテストを実行します.
# runs all tests
cargo test
# runs only library tests
cargo test --lib
# runs only documentation tests
cargo test --doc
テストが失敗するとプログラムが失敗する.
#[test]
fn test_that_fails() {
        panic!("Make this test fail");
}

主張


// when you want to ensure that some condition in a test evaluates to true
assert!(4 == 2 + 2);
// compare two arguments for equality
assert_eq!(4, 2 + 2);
// compare two arguments for inequality
assert_ne!(4, 2 + 1);

カスタムメッセージ


pub fn greeting(name: &str) -> String {
    String::from("Hello!")
}

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

    #[test]
    fn greeting_contains_name() {
        let result = greeting("Carol");
        assert!(
            result.contains("Carol"),
            "Greeting did not contain name, value was `{}`",
            result
        );
    }
}

パニックテスト


pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 || value > 100 {
            panic!("Guess value must be between 1 and 100, got {}.", value);
        }

        Guess { value }
    }
}

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

    #[test]
    #[should_panic]
    fn greater_than_100() {
        Guess::new(200);
    }
}

テスト結果


#[cfg(test)]
mod tests {
    #[test]
    fn it_works() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("two plus two does not equal four"))
        }
    }
}

ドキュメント


用途CommonMark .

ドキュメントのテスト



セクション



ドキュメントを生成する


# '--open' opens generated documentation in the browser
cargo doc --open

演習


パスワードコンバータ-パート2


タスク


ライブラリからライブラリへのリファクタプログラム.
内部converters モジュールを作成するConverter 特性
この特徴を実装するKasperskyPasswordManager 内部converters::kaspersky モジュールです.
ドキュメント、docテスト、およびKasperskyPasswordManager .
メイクKasperskyPasswordManager ジェネリック.

解決策


Password converter lib

アドベンチャーゲーム-パート2

タスク


からの冒険ゲームに戦い場面を加えてください...
敵がいるだろう.敵は攻撃ダメージ範囲と健康を持っている.
プレイヤーは攻撃ダメージ範囲と健康(各シーン別に)があります.
プレイヤーはアクションを選択する
  • 攻撃-攻撃ダメージ範囲でランダムな値は、敵に配られます.敵が死亡した場合、プレーヤーは次のシーンに進む.さもなければ、敵は反撃します.プレイヤーが生き残る場合は、オプションが更新された健康で繰り返されます.プレーヤーが死ぬ場合は、ゲームオーバーです.
  • 逃げる-別のシーンに行く
  • シーンはコードを共有します.
    ドキュメントとテストを追加します.

    解決策


    Adventure game
    Githubで私の学習錆レポをチェック!

    PETR 7555 / 学習錆