ควบคุม Scope และ Privacy ด้วย Module
ในส่วนนี้ เราจะพูดถึง module และส่วนอื่นของระบบ module ได้แก่ path ที่
ให้คุณตั้งชื่อ item, keyword use ที่นำ path เข้า scope และ keyword pub
เพื่อทำให้ item เป็น public เราจะพูดถึง keyword as, package ภายนอก และ
glob operator ด้วย
Cheat Sheet ของ Module
ก่อนเราเข้าสู่รายละเอียดของ module และ path ที่นี่เราให้ reference ด่วน
เกี่ยวกับวิธีที่ module, path, keyword use และ keyword pub ทำงานใน
compiler และวิธีที่นักพัฒนาส่วนใหญ่จัดระเบียบโค้ดของพวกเขา เราจะผ่าน
ตัวอย่างของกฎเหล่านี้แต่ละข้อตลอดบทนี้ แต่นี่เป็นที่ดีที่จะอ้างถึงเพื่อ
เตือนความจำว่า module ทำงานยังไง
- เริ่มจาก crate root: เมื่อ compile crate compiler มองในไฟล์ crate root ก่อน (โดยปกติ src/lib.rs สำหรับ library crate และ src/main.rs สำหรับ binary crate) สำหรับโค้ดที่จะ compile
- ประกาศ module: ในไฟล์ crate root คุณประกาศ module ใหม่ได้ สมมติคุณ
ประกาศ module “garden” ด้วย
mod garden;Compiler จะมองหาโค้ดของ module ในที่เหล่านี้:- Inline ภายใน curly bracket ที่แทน semicolon ตาม
mod garden - ในไฟล์ src/garden.rs
- ในไฟล์ src/garden/mod.rs
- Inline ภายใน curly bracket ที่แทน semicolon ตาม
- ประกาศ submodule: ในไฟล์ใด ๆ นอกจาก crate root คุณประกาศ submodule
ได้ เช่น คุณอาจประกาศ
mod vegetables;ใน src/garden.rs Compiler จะมองหาโค้ดของ submodule ภายใน directory ที่ตั้งชื่อตาม module พ่อ ใน ที่เหล่านี้:- Inline ตามด้วย
mod vegetablesตรง ๆ ภายใน curly bracket แทน semicolon - ในไฟล์ src/garden/vegetables.rs
- ในไฟล์ src/garden/vegetables/mod.rs
- Inline ตามด้วย
- Path ไปยังโค้ดใน module: เมื่อ module เป็นส่วนหนึ่งของ crate ของคุณ
คุณอ้างถึงโค้ดใน module นั้นจากที่อื่นใดใน crate เดียวกันได้ ตราบที่กฎ
privacy อนุญาต โดยใช้ path ไปยังโค้ด เช่น type
Asparagusใน module garden vegetables จะถูกหาที่crate::garden::vegetables::Asparagus - Private vs. public: โค้ดภายใน module เป็น private จาก module พ่อ
โดย default ในการทำให้ module เป็น public ประกาศด้วย
pub modแทนmodในการทำให้ item ภายใน module public เป็น public ด้วย ใช้pubก่อนการประกาศ - Keyword
use: ภายใน scope keyworduseสร้าง shortcut ของ item เพื่อลดการเขียน path ยาว ๆ ซ้ำ ใน scope ใดก็ตามที่อ้างถึงcrate::garden::vegetables::Asparagusได้ คุณสร้าง shortcut ด้วยuse crate::garden::vegetables::Asparagus;ได้ และจากนั้นคุณเขียนแค่Asparagusเพื่อใช้ type นั้นใน scope ก็พอ
ที่นี่ เราสร้าง binary crate ชื่อ backyard ที่แสดงกฎเหล่านี้ Directory
ของ crate ก็ชื่อ backyard มีไฟล์และ directory เหล่านี้:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
ไฟล์ crate root ในกรณีนี้คือ src/main.rs และมี:
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {plant:?}!");
}
บรรทัด pub mod garden; บอก compiler ให้ include โค้ดที่มันเจอใน
src/garden.rs ซึ่งคือ:
pub mod vegetables;
ที่นี่ pub mod vegetables; หมายความว่าโค้ดใน src/garden/vegetables.rs
ถูก include ด้วย โค้ดนั้นคือ:
#[derive(Debug)]
pub struct Asparagus {}
ทีนี้มาเข้าสู่รายละเอียดของกฎเหล่านี้และแสดงพวกมันในการใช้งาน!
จัดกลุ่มโค้ดที่ผูกกันใน Module
Module ให้เราจัดระเบียบโค้ดภายใน crate สำหรับความอ่านง่ายและการใช้ซ้ำ ได้ง่าย Module ยังให้เราควบคุม privacy ของ item ด้วย เพราะโค้ดภายใน module เป็น private โดย default Private item เป็นรายละเอียด implementation ภายในที่ไม่มีให้ภายนอกใช้ เราเลือกทำให้ module และ item ภายในเป็น public ได้ ซึ่งเปิดเผยพวกมันให้โค้ดภายนอกใช้และพึ่งพา
เป็นตัวอย่าง มาเขียน library crate ที่ให้ functionality ของร้านอาหาร เรา จะประกาศ signature ของฟังก์ชัน แต่ทิ้ง body ว่างไว้ เพื่อโฟกัสที่การจัด ระเบียบของโค้ดมากกว่าการ implement ร้านอาหาร
ในอุตสาหกรรมร้านอาหาร บางส่วนของร้านอาหารถูกอ้างถึงเป็น front of house และอื่น ๆ เป็น back of house Front of house คือที่ที่ลูกค้าอยู่ ซึ่งรวม ที่ที่ host จัดที่นั่งลูกค้า, server รับ order และเงิน, และ bartender ทำ เครื่องดื่ม Back of house คือที่ที่ chef และพ่อครัวทำงานในครัว, คนล้าง จานทำความสะอาด และผู้จัดการทำงาน administrative
ในการ structure crate ของเราในแบบนี้ เราจัดระเบียบฟังก์ชันของมันเป็น
module ซ้อน สร้าง library ใหม่ชื่อ restaurant โดยรัน
cargo new restaurant --lib จากนั้นป้อนโค้ดใน Listing 7-1 เข้า
src/lib.rs เพื่อประกาศ module และ signature ฟังก์ชัน — โค้ดนี้คือส่วน
front of house
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() {}
}
}
front_of_house ที่มี module อื่นที่จากนั้นมีฟังก์ชันเราประกาศ module ด้วย keyword mod ตามด้วยชื่อของ module (ในกรณีนี้
front_of_house) body ของ module จากนั้นไปภายใน curly bracket ภายใน
module เราวาง module อื่นได้ เหมือนในกรณีนี้กับ module hosting และ
serving Module ยังเก็บ definition สำหรับ item อื่นได้ เช่น struct,
enum, constant, trait และเหมือนใน Listing 7-1 ฟังก์ชัน
ด้วยการใช้ module เราจัดกลุ่ม definition ที่ผูกกันเข้าด้วยกัน และตั้งชื่อ ว่าทำไมพวกมันผูกกัน โปรแกรมเมอร์ที่ใช้โค้ดนี้นำทางโค้ดตามกลุ่ม แทนต้อง อ่านผ่าน definition ทั้งหมด ทำให้ง่ายขึ้นที่จะหา definition ที่เกี่ยวข้อง กับพวกเขา โปรแกรมเมอร์ที่เพิ่ม functionality ใหม่ให้โค้ดนี้รู้ว่าจะวาง โค้ดที่ไหน เพื่อให้โปรแกรมจัดระเบียบ
ก่อนหน้านี้ เราเอ่ยว่า src/main.rs และ src/lib.rs เรียกว่า crate
root เหตุผลของชื่อพวกมันคือเนื้อหาของหนึ่งในสองไฟล์เหล่านี้ ประกอบ
เป็น module ชื่อ crate ที่ root ของโครงสร้าง module ของ crate รู้จัก
ในชื่อ module tree
Listing 7-2 แสดง module tree สำหรับโครงสร้างใน Listing 7-1
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
tree นี้แสดงว่า module บางตัวซ้อนภายใน module อื่นอย่างไร เช่น hosting
ซ้อนภายใน front_of_house tree ยังแสดงว่า module บางตัวเป็น sibling
หมายความว่าพวกมันประกาศใน module เดียวกัน — hosting และ serving เป็น
sibling ที่ประกาศภายใน front_of_house ถ้า module A ถูกบรรจุภายใน module
B เราบอกว่า module A เป็น ลูก ของ module B และ module B เป็น พ่อ ของ
module A สังเกตว่าทั้ง module tree มี root อยู่ภายใต้ module implicit ที่
ชื่อ crate
module tree อาจเตือนคุณถึง directory tree ของ filesystem บนคอมพิวเตอร์ ของคุณ — นี่เป็นการเปรียบเทียบที่เหมาะมาก! เหมือนกับ directory ใน filesystem คุณใช้ module จัดระเบียบโค้ดของคุณ และเหมือนกับไฟล์ใน directory เราต้องการวิธีหา module ของเรา