แยก Module ไปคนละไฟล์
ที่ผ่านมา ตัวอย่างทั้งหมดในบทนี้ประกาศ module หลายตัวในไฟล์เดียว เมื่อ module ใหญ่ คุณอาจอยากย้าย definition ของพวกมันไปไฟล์แยก เพื่อทำให้โค้ด ง่ายต่อการ navigate
เช่น เริ่มจากโค้ดใน Listing 7-17 ที่มี module ร้านอาหารหลายตัว เราจะดึง module เข้าไฟล์ แทนการมี module ทั้งหมดประกาศในไฟล์ crate root ในกรณีนี้ ไฟล์ crate root คือ src/lib.rs แต่ขั้นตอนนี้ก็ใช้ได้กับ binary crate ที่ ไฟล์ crate root คือ src/main.rs
ก่อนอื่น เราจะดึง module front_of_house ออกไปไฟล์ของมันเอง ลบโค้ดภายใน
curly bracket สำหรับ module front_of_house เหลือแค่การประกาศ
mod front_of_house; เพื่อให้ src/lib.rs มีโค้ดที่แสดงใน Listing 7-21
หมายเหตุว่านี่จะ compile ไม่ผ่านจนกว่าเราจะสร้างไฟล์ src/front_of_house.rs
ใน Listing 7-22
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
front_of_house ที่ body จะอยู่ใน src/front_of_house.rsถัดไป วางโค้ดที่อยู่ใน curly bracket เข้าไฟล์ใหม่ชื่อ
src/front_of_house.rs ดังที่แสดงใน Listing 7-22 Compiler รู้ว่าจะมอง
ในไฟล์นี้ เพราะมันเจอการประกาศ module ใน crate root ที่ชื่อ
front_of_house
pub mod hosting {
pub fn add_to_waitlist() {}
}
front_of_house ใน src/front_of_house.rsหมายเหตุว่าคุณต้อง load ไฟล์โดยใช้การประกาศ mod ครั้งเดียว ใน module
tree ของคุณ เมื่อ compiler รู้ว่าไฟล์เป็นส่วนของโปรเจกต์ (และรู้ว่าโค้ด
อยู่ที่ไหนใน module tree เพราะคุณวาง statement mod ที่ไหน) ไฟล์อื่นใน
โปรเจกต์ของคุณควรอ้างถึงโค้ดของไฟล์ที่ load โดยใช้ path ไปยังตำแหน่งที่มัน
ถูกประกาศ ดังที่ครอบคลุมในส่วน
“Path สำหรับอ้างถึง Item ใน Module Tree” พูดอีก
อย่าง mod ไม่ใช่ operation “include” ที่คุณอาจเคยเห็นในภาษาโปรแกรม
อื่น
ถัดไป เราจะดึง module hosting ออกไปไฟล์ของมันเอง กระบวนการต่างกันนิด
หน่อย เพราะ hosting เป็น module ลูกของ front_of_house ไม่ใช่ของ
module root เราจะวางไฟล์สำหรับ hosting ใน directory ใหม่ที่จะตั้งชื่อ
ตามบรรพบุรุษใน module tree ในกรณีนี้ src/front_of_house
ในการเริ่มย้าย hosting เราเปลี่ยน src/front_of_house.rs ให้มีแค่การ
ประกาศของ module hosting:
pub mod hosting;
จากนั้น เราสร้าง directory src/front_of_house และไฟล์ hosting.rs
เพื่อมี definition ที่ทำใน module hosting:
pub fn add_to_waitlist() {}
ถ้าแทนเราใส่ hosting.rs ใน directory src compiler จะคาดว่าโค้ด
hosting.rs อยู่ใน module hosting ที่ประกาศใน crate root และไม่
ประกาศเป็นลูกของ module front_of_house กฎของ compiler สำหรับไฟล์ไหนที่
จะเช็คสำหรับโค้ดของ module ไหน หมายความว่า directory และไฟล์ match กับ
module tree ใกล้กว่า
Path ไฟล์ทางเลือก
ที่ผ่านมาเราครอบคลุม path ไฟล์ที่ idiomatic ที่สุดที่ Rust compiler ใช้
แต่ Rust รองรับสไตล์ path ไฟล์เก่ากว่าด้วย สำหรับ module ชื่อ
front_of_house ที่ประกาศใน crate root compiler จะมองหาโค้ดของ module
ใน:
- src/front_of_house.rs (สิ่งที่เราครอบคลุม)
- src/front_of_house/mod.rs (สไตล์เก่ากว่า ยังรองรับ path)
สำหรับ module ชื่อ hosting ที่เป็น submodule ของ front_of_house
compiler จะมองหาโค้ดของ module ใน:
- src/front_of_house/hosting.rs (สิ่งที่เราครอบคลุม)
- src/front_of_house/hosting/mod.rs (สไตล์เก่ากว่า ยังรองรับ path)
ถ้าคุณใช้ทั้งสองสไตล์สำหรับ module เดียวกัน คุณจะได้ compiler error การใช้สไตล์ผสมกันสำหรับ module ต่างกันในโปรเจกต์เดียวกันอนุญาต แต่อาจ ทำให้คนที่ navigate โปรเจกต์ของคุณสับสน
ข้อเสียหลักของสไตล์ที่ใช้ไฟล์ชื่อ mod.rs คือโปรเจกต์ของคุณอาจจบลงด้วย ไฟล์ชื่อ mod.rs หลายตัว ซึ่งอาจสับสนเมื่อคุณเปิดพวกมันใน editor พร้อม กัน
เราย้ายโค้ดของแต่ละ module ไปไฟล์แยก และ module tree ยังเหมือนเดิม การ
เรียกฟังก์ชันใน eat_at_restaurant จะทำงานได้โดยไม่ต้องแก้ใด ๆ แม้
definition อยู่ในไฟล์ต่างกัน เทคนิคนี้ให้คุณย้าย module ไปไฟล์ใหม่เมื่อ
พวกมันใหญ่ขึ้น
หมายเหตุว่า statement pub use crate::front_of_house::hosting ใน
src/lib.rs ก็ไม่ได้เปลี่ยน และ use ไม่มีผลต่อไฟล์ไหนที่ถูก compile
เป็นส่วนของ crate Keyword mod ประกาศ module และ Rust มองในไฟล์ที่ชื่อ
เดียวกับ module สำหรับโค้ดที่ไปใน module นั้น
สรุป
Rust ให้คุณแบ่ง package เป็นหลาย crate และ crate เป็นหลาย module เพื่อ
ให้คุณอ้างถึง item ที่ประกาศใน module หนึ่งจากอีก module ได้ คุณทำได้โดย
ระบุ absolute หรือ relative path Path เหล่านี้นำเข้า scope ด้วย statement
use ได้ เพื่อให้คุณใช้ path สั้นกว่าสำหรับการใช้ item หลายครั้งใน scope
นั้น โค้ด module เป็น private โดย default แต่คุณทำให้ definition เป็น
public ได้ โดยเพิ่ม keyword pub
ในบทถัดไป เราจะดูโครงสร้างข้อมูล collection ใน standard library ที่คุณใช้ ในโค้ดที่จัดระเบียบดีของคุณได้