By Weerasak Chongnguluam

Learn Rust: Package, Crate and Module


Pacakge

เวลาเราใช้ Cargo จัดการ Rust project นั้น ตอนที่เรา cargo new project เราจะได้โครงสร้าง project ประมาณนี้

.
├── Cargo.toml
└── src
    └── main.rs

ซึ่ง Cargo จะเรียกโครงสร้างแบบนี้ว่าเป็น 1 package

ทีนี้ใน 1 package Cargo จะแบ่งโค้ดเราออกเป็น crate เช่นจากที่สร้างมาให้ตามรูปเราจะมี root crate คือ crate ที่ชื่อเดียวกันกับ package (project) นั่นแหละ ถ้าเราสร้าง package โดยคำสั่ง cargo new hello ก็จะได้ root crate ชื่อ hello

Crate

ใน 1 crate ก็จะมีได้หลาย ๆ submodule ส่วนตัว root crate จะมองว่าเป็น root module ด้วยก็ได้ ซึ่ง submodule จะช่วยแบ่ง namespace ในการประกาศ function, namespace, type, var และ const ต่าง ๆ

ในโค้ดที่ cargo new สร้างให้เราจะเห็นแค่ src/main.rs ซึ่งจะถูกเรียกว่า binary crate เพราะว่า build แล้วได้ binary ที่เอามา execute ได้

นอกจากนั้นใน 1 package เรายังสามารถสร้าง library crate ที่จะถูกเรียกใช้ผ่าน binary crate หรือ publish ไปใช้ package อื่น ๆ ใช้ได้ด้วยโดยการเพิ่ม dependency ใน Cargo.toml โดยโค้ด library crate จะถูกเก็บใน src/lib.rs

เช่น ใน lib.rs มีโค้ดแบบนี้

pub fn say_hello() {
    println!("Hello, world!");
}

และใน main.rs มีโค้ดแบบนี้

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

เราจะเห็นว่าเราใช้ hello::say_hello() ใน main.rs ซึ่ง hello คือชื่อ crate ที่เราสร้างขึ้นมา และ say_hello คือ function ที่อยู่ใน crate นั้น ๆ เราต้องใส่ pub ด้วยเพื่อให้สามารถเรียกใช้จาก crate อื่น ๆ ได้ ถ้าไม่ใส่เป็น private เรียกได้แต่ใน crate ตัวเอง

Module

เราสามารถสร้าง sub module เพิ่มใน crate ได้โดยใช้ keyword mod เช่น

pub mod guest {
    pub fn say_hello() {
        println!("Hello, guest!");
    }
}

ซึ่งเราต้องประกาศให้เป็น pub ให้ mod ด้วยเหมือนกันเพื่อให้สามารถเรียกใช้จาก crate อื่น ๆ ได้

โค้ดใน main ต้องการเรียกใช้ module นี้ ก็ใช้แบบนี้

fn main() {
    hello::guest::say_hello();
}

นอกจากเราจะประกาศสิ่งต่าง ๆ ภายใต้ module ไว้ใน block {} ของ module แล้ว เรายังสามารถแยกโค้ด module ออกเป็นอีกไฟล์ได้ โดยสร้างไฟล์ชื่อเดียวกันกับ module เช่น src/guest.rs และใส่โค้ดนี้ลงไป

pub fn say_hello() {
    println!("Hello, guest!");
}

ส่วนใน root module src/lib.rs ก็จะเหลือแค่ประกาศ module แบบนี้

pub mod guest;

นอกจากท่านี้แล้ว เรายังสามารถแยก module ออกเป็น folder ใหม่ได้เช่น src/guest/mod.rs และใส่โค้ดนี้ลงไป

pub fn say_hello() {
    println!("Hello, guest!");
}

เพื่อที่จะสามารถสร้าง sub module ของ guest ได้อีกชั้นนึงง่ายๆ เช่นเราอยากสร้าง module msg ใน guest ก็ทำได้โดย

  • ประกาศ pub mod msg; ใน src/guest/mod.rs จะใส่โค้ดใน block ตรง ๆ ก็ได้ หรือ
  • สร้างไฟล์ src/guest/msg.rs และใส่โค้ดลงไป หรือ
  • สร้าง folder src/guest/msg และสร้าง src/guest/msg/mod.rs และใส่โค้ดลงไป

Use module

เวลาเรียกใช้สิ่งที่อยู่ใน create อื่น ๆ หรือ sub module ของ crate อื่น ๆ ที่ประกาศเป็น public เราเห็นตัวอย่างไปแล้วว่าให้เริ่มจากชื่อ crate แล้วใช้ :: ค่อย ๆ เข้าถึงไปในแต่ละ sub module ตามลำดับ

แต่ถ้าเราไม่อยากเขียนยาว ๆ ทุกครั้งเราสามารถใช้ use keyword มาช่วยได้ เช่น

use hello::guest::say_hello;

fn main() {
    say_hello();
}

เราจะเห็นว่าเราใช้ use กับ hello::guest::say_hello แล้วเราสามารถเรียกใช้ say_hello() ได้โดยตรง

หรือเราจะ use แค่ module ที่เราต้องการเฉย ๆ ก็ได้เช่น

use hello::guest;

fn main() {
    guest::say_hello();
}

สรุป

  • Package คือโครงสร้างของ Rust project ที่เราสร้างด้วย Cargo
  • Crate คือโค้ดที่เราสร้างขึ้นมาเอง ซึ่งอาจจะเป็น binary crate หรือ library crate
  • Module คือส่วนย่อย ๆ ของ crate ที่เราสร้างขึ้นมา ที่เราใช้เพื่อแบ่ง namespace ของ function, namespace, type, var และ const ต่าง ๆ
  • เราสามารถใช้ use keyword เพื่อเรียกใช้ module ที่เราต้องการโดยไม่ต้องเขียนชื่อ crate และ module ยาว ๆ ทุกครั้ง
  • เราสามารถแยก module ออกเป็นไฟล์หรือ folder ได้เพื่อให้โค้ดเราสะดวกขึ้น
  • เราสามารถสร้าง sub module ของ module ได้โดยใช้ mod keyword และสร้างไฟล์หรือ folder ใหม่ได้