[RUST] 7. 패키지, 크레이트, 모듈로 프로젝트 관리하기

    • 패키지: 크레이트를 빌드, 테스트, 공유할 수 있는 카고의 기능
    • 크레이트: 라이브러리나 실행 파일을 생성하는 모듈의 트리(tree)
    • 모듈과 use: 코드의 구조와 범위, 그리고 경로의 접근성을 제어하는 기능
    • 경로(path): 구조체, 함수, 혹은 모듈 등의 이름을 결정하는 방식

    패키지와 크레이트

    크레이트는 하나의 바이너리 혹은 라이브러리다. 크레이트 루트(root)는 러스트 컴파일러가 컴파일을 시작해서 크레이트의 루트 모듈을 만들어내는 소스 파일이다. 패키지는 이 크레이트를 빌드하는 방법을 서술하는 Cargo.toml 파일을 갖는다.

    패키지를 생성하게되면 (cargo new 명령) 카고는 Cargo.toml 파일을 생성해 패키지를 만들어 낸다.

    모듈을 이용한 범위와 접근성 제어

    모듈(module)은 크레이트의 코드를 그룹화해서 가독성과 재사용성을 향상하는 방법이다. 아이템의 접근성을 결정한다.

    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() {}
        }
    }

    create 루트 모듈 하위에 front_of_house 모듈이 있으며 하위에 hosting과 serving 모듈을 추가됨을 확인할 수 있다. 이는 파일 시스템의 디렉터리 트리와 유사하다.

    경로를 이용해 모듈 트리의 아이템 참조하기

    • 절대 경로(absolute path): 크레이트 이름이나 crate 리터럴을 이용해 크레이트 루트부터 시작하는 경로
    • 상대 경로(relative path): 현재 모듈로부터 시작하며 self, super 혹은 현재 모듈의 식별자를 이용한다.
    mod front_of_house {
        mod hosting {
            fn add_to_waitlist() {}
        }
    }
    
    pub fn eat_at_restaurant() {
        //절대 경로
        crate::front_of_house::hosting::add_to_waitlist();
    
        //상대 경로
        front_of_house::hosting::add_to_waitlist();
    }

    위 코드는 컴파일하면 에러메시지가 출력된다. 경로는 올바르지만 hosting 모듈이 비공개이므로 호출할 수 없다는 뜻이다.

    pub 키워드로 경로 공개하기

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

    hosting 모듈을 pub 키워드를 추가하여 공개 모듈로 변경하였지만. 아직 에러메시지가 출력된다. hosting 모듈만 공개된다고 아이템들이 전부 공개되는건 아니기 때문이다. 그래서 함수에도 pub 키워드를 추가하여 함수를 공개처리하여 컴파일 되도록 수정하였다.

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

    super로 시작하는 상대 경로

    super 키워드는 부모 모듈부터 시작한다는 뜻이다. 파일시스템의 .. 문법과 동일하다

    fn serve_order() {}
    
    mod back_of_house {
        fn fix_incorrect_order() {
            cook_order();
            super::serve_order();
        }
    
        fn cook_order() {}
    }

    구조체와 열거자 공개하기

    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("복숭아"),
                }
            }
        }
    }
    
    pub fn eat_at_restaurant() {
        // 여름에 아침 식사로 호밀빵을 주문
        let mut meal = back_of_house::Breakfast::summer("호밀빵");
        // 고객이 마음을 바꿔 빵 종류를 바꿈
        meal.toast = String::from("밀빵");
        println!("{} 토스트로 주세요", meal.toast);
    
        // 주석 해제시 컴파일 오류
        // 고객은 식사와 계절 과일을 선택할 수 없다.
        // meal.seasonal_fruit = String::from("블루베리");
    }

    toast와 seasonal_fruit 처럼 pub 키워드를 통해 공개 여부를 결정할 수 있다. 이에 반해 열거자는 공개하면 모든 열것값이 공개된다.

    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;
    }

    use 키워드로 경로를 범위로 가져오기

    use 키워드로 경로를 현재 범위로 가져오면 경로의 아이템이 현재 범위의 아이템인 것처럼 호출할 수 있다.

    mod front_of_house {
        pub mod hosting {
            pub fn_ add_to_waitlist() {}
        }
    }
    
    //둘다 같은 동작을 한다
    use crate::front_of_house::hosting;
    use self::front_of_house::hosting;
    
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
    }

    관용적인 경로 사용하기

    mod front_of_house {
        pub mod hosting {
            pub fn_ add_to_waitlist() {}
        }
    }
    
    use self::front_of_house::hosting::add_to_waitlist;
    
    pub fn eat_at_restaurant() {
        add_to_waitlist();
    }

    위 예제는 add_to_waitlist 함수가 어디에 정의되어있는지 파악하기가 어렵다. 그래서 관용적으로 부모 모듈을 범위로 가져와서 부모 모듈의 이름과 함수 이름을 조합해서 호출하면 로컬에 정의된 것이 아니라는 점을 좀더 명확하게 한다. 다음 같은 이름을 가진 타입을 다른 부모에 정의될 경우 현재 범위로 가져오는 예제이다.

    use std::fmt;
    use std::io;
    
    fn function1() -> fmt::Result {
        //생략
    }
    
    fn function2() -> io::Result<()> {
        //생략
    }

    as 키워드로 새로운 이름 부여하기

    use 구문으로 같은 이름을 가진 두 타입을 현재 범위로 가져오는 다른 방법은 as 키워드를 이용해 새로운 이름을 부여하는 방법이있다.

    use std::fmt::Result
    use std::io::Result as IoResult;
    
    fn function1() -> Result {
        //생략
    }
    
    fn function2() -> IoResult<()> {
        //생략
    }

    pub use 키워드로 이름을 다시 보내기

    use 키워드 앞에 pub 키워드를 조합하면 다른 범위로 내보낼 수 있다. 이를 다시 내보내기(re-exporting)이라고 한다.

    외부 패키지의 사용

    외부 패키지를 사용할때는 Cargo.toml 파일에 의존성을 추가하여 해당 패키지를 내려받도록 한다. 그리고 use 구문을 이용해 범위로 가져와서 사용하면 된다.

    중첩 경로로 use 목록을 깔끔하게 유지하기

    중첩된 경로는 중첩 경로를 통해 한줄의 코드로 구현할 수 있다.

    use std::io;
    use std::cmp::Ordering;
    // 다음과 같이 한줄로 사용 가능
    use std::{io, cmp::Ordering};
    
    use std::io;
    use std::io::Write;
    // 경로가 일치하는 경우 slef 키워드를 사용할 수도있다.
    use std::io::{self, Write};

    글롭 연산자

    어떤 경로의 공개 아이템을 모두 현재 범위로 가져오려면 *연산자를 사용하면 된다.

    use std::collections::*;

    모듈을 다른 파일로 분리하기

    모듈의 크기가 커지면 코드탐색을 위해 파일을 분리하는게 효과적이다. 다음은 이전 예제를 파일 분리한 예제이다.

    // src/lib.rs
    mod front_of_house;
    
    pub use crate::front_of_house::hosting;
    
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
        hosting::add_to_waitlist();
        hosting::add_to_waitlist();
    }
    
    // 코드 블럭{} 을 통해 구현
    // src/front_of_house.rs
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
    
    // 정의만 한경우 모듈과 같은 이름을 가진 파일에서 컨텐츠를 가져옴
    // src/front_of_house.rs
    pub mod hosting;
    
    // src/front_of_house/hostring.rs
    pub fn add_to_waitlist() {}

    요약

    • 러스트는 패키지를 크레이트로 정리하며 크레이트는 모듈을 구성한다.
    • 한 모듈에 정의된 아이템을 다른 모듈에서 참조 가능
    • use 키워드를 통해 현재 범위를 가져올수 있다.
    • 모듈 코드는 기본적으로 비공개지만, pub 키워드를 추가해 공개 가능
    반응형

    댓글

    Designed by JB FACTORY