Rust가 처음이 아닌사람들은 제목의 Packages Crates and Modules 가 각각 것인지 대충은 알것입니다. 프로그램을 만들때 재사용되는 코드들을 정리해서 만들어서 재사용 및 다른사람과 공유함으로써 전체적인 생산성 향상 및 커뮤니티 전체 발전을 위한 개념이라고 보면됩니다. 거창하게 말했지만, 그냥 재사용되는 코드, 가져다 쓸수있는 코드 정도로 이해하면 됩니다.
Crates < Packages
Crate가 이 코드들의 최소단위 입니다. Rust에서 컴파일할때 컴파일러가 한번에 보는 범위가 Crate 단위입니다.
binary crate and library crate
Binary crate는 실행가능한 형태로 컴파일되는것이고, Library crate는 실행이 불가능하고 작성시 main 함수가 존재하지 않죠. 우리가 난수생성을위해 사용했던 rand crate가 그런 예시입니다 소스코드에서 import하여 함수를 사용할수있었죠.
또 다른 예시로는 우리가 cargo new project-name 으로 프로젝트를 만들면 ./src/main.rs 파일이 생기는데 이것이 실행가능한 바이너리 crate입니다 안에는 main함수가 존재하죠. 또 루트디렉토리에 ./cargo.toml파일이있는데 이것이 패키지를 관리해주는 파일 입니다. 프로젝트를 만들면 기본적으로 crate 1개와 package 1개가 생성된다고 보시면됩니다.
프로젝트는 기본적으로 패키지를 갖고있다는걸 이해하셨을겁니다. 기본프로젝트에 ./src/lib.rs파일을 만들면 이 패키지는 binary crate와 library crate를 둘 다 가진 패키지가 됩니다. 또 패키지는 ./src/bin 경로에 파일을 둠으로써 여러 binary crates를 가질수있는것을 기억하고 넘어가겠습니다.
Control Scope and Privacy with Modules
- use: path를 scope로 가져오는 키워드
- pub: 아이템을 public화 시키는 키워드
- as
- external packages
- glob operator
Start from the crate loot
crate를 컴파일하면, 컴파일러는 먼저 src/main.rs 와 src/lib.rs파일을 먼저 확인합니다. 이를 root files 라고합니다.
Declaring modules
root 파일에서, 새로운 모듈을 선언할수 있습니다. 만약 mod garden; 구문이 있으면, 컴파일러는 해당모듈의 코드를 이구문이 있는곳에서 처리합니다
- src/garden.rs
- src/garden/mod.rs
Declaring submodules
submodule은 루트파일이 아닌 모듈내에서 사용하면됩니다 예를들어 위에서 언급했던 garden.rs파일에서 mod vegetables 구문을 작성하면 아래 경로를 확인합니다.
- src/garden/vegetables.rs
- src/garden/vegetables/mod.rs
Private vs Public
기본적으로 module은 private이 기본상태입니다. 사용자가 사용해야하는 함수나 변수에는 pub키워드로 명시가 되어야합니다. C++의 캡슐화개념이라고 이해하면됩니다. 모듈을 사용할때 사용자가 굳이 알필요없는 함수들은 숨겨서 생산성을 올립니다. 쓸필요가 없는 함수들이 다 공개되어있으면 헷갈리기 쉽고 잘못사용할 여지도 늘어납니다.
use 키워드
use키워드는 긴 scope를 생략할때 사용합니다. 예를들어, crate::garden::vegetables::Asparagus 경로처럼 깊은곳에 Asparagus가 있다고 존재하면, use crate::garden::vegetables::Asparagus; 구문을 작성해서 이후로는 Asparagus만 써도 이게 무엇인지 알수있고 사용할수 있습니다.
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {plant:?}!");
}
파일구조를 활용한 예시
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
위와같은 파일경로가있으면 우리 프로젝트 폴더인 backyard가 메인 crate이름이 됩니다.
경로는 뒷마당 > 가든 > 채소 순서입니다.
루트 crate 즉 backyard crate의 파일인 main.rs에 아래처럼 적습니다. Asparagus를 use 키워드를 사용해 직접 쓸수있도록 해주고, garden crate는 pub으로 불러옵니다
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {plant:?}!");
}
./src/garden.rs 파일에서는 아래 구문을 작성하여 부모 crate (backyard) 에서 vegetable을 사용할수있게 공개합니다.
pub mod vegetables;
./src/garden/vegetables.rs 파일에서는 아래처럼 Asparagus 구조체를 pub으로 선언하여 상위 crates에서 사용할수있게 공개합니다.
#[derive(Debug)]
pub struct Asparagus {}
기본적으로 private 상태이기때문에 곡 해줘야합니다. 멤버변수나 함수도 let fn 키워드 앞에 pub을 붙여서 공개시킬수 있습니다.
mod
위에 예시코드들에서 mod키워드를 사용하고있었는데요. mod는 모듈의 줄임말이고 C++에서의 네임스페이스와 비슷한기능을합니다.
차이점이라면 C++은 클래스차원에서 캡슐화가 존재했는데 Rust에서는 mod, struct, fn , 그리고 let 변수들까지 각각에 공개 비공개 설정을 할수있습니다.
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
fn main() {
front_of_house::hosting::add_to_waitlist();
}
Module path
지금까지는 하위 crate에있는걸 루트나 상위에서 접근하는 방법을 알아봤는데, 하위에서도 상위로 갈수있습니다.
- crate:: 을사용하면 루트 crate에 접근이가능하고
- super:: 은 현재 루트에서 한단계 위로 올라가는겁니다.
- self::는 현재모듈인데 평상시엔 쓰지않지만 만약 다른모듈에 같은이름의 아이템이있으면 이걸로 구분이 가능합니다.
pub for structs and enums
enum은 pub아니면 쓰기불편하기에 기본적으로 내부 값들이 public상태 하지만 enum 키워드앞에는 pub을 붙여줘야함
struct는 struct자체에만 pub을쓰면안되고 내부 멤버변수나 멤버함수들 모두 기본값이 private이라 pub을 붙여줘야함.