Rust 4 - 모듈, 크레이트, 테스트, 문서화

35963 단어 rust

Rust 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
hosting.rsserving.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 함수를 호출할 수 있으려면 함수가 공개되어야 합니다. 또한 해당 기능으로 이어지는 모듈은 공개되어야 합니다.

함수를 공개합니다.

// 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()`
}

종속성


crates.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 addercargo 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에 대해 이 특성을 구현합니다.
KasperskyPasswordManager에 대한 문서, 문서 테스트 및 단위 테스트를 작성합니다.
KasperskyPasswordManager를 일반으로 만듭니다.

해결책



Password converter lib

어드벤처 게임 - 파트 2



에서 어드벤처 게임에 전투 장면을 추가합니다.

적이있을 것입니다. 적에게는 공격 피해 범위와 체력이 있습니다.

플레이어는 공격 피해 범위와 체력이 있습니다(각 장면마다 별도).

플레이어는 행동을 선택합니다:
  • 공격 - 공격 피해 범위의 무작위 값이 적에게 적용됩니다. 적이 죽으면 플레이어는 다음 장면으로 진행합니다. 그렇지 않으면 적이 반격합니다. 플레이어가 살아남으면 업데이트된 체력으로 옵션이 반복됩니다. 플레이어가 죽으면 게임이 종료됩니다.
  • 도망가다 - 다른 장면으로 이동

  • 장면은 특성을 사용하여 코드를 공유합니다.

    설명서 및 테스트를 추가합니다.

    해결책



    Adventure game

    GitHub에서 내 학습 Rust 저장소를 확인하세요!


    펫7555 / 학습-녹


    좋은 웹페이지 즐겨찾기