Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

การแยกโมดูลออกเป็นหลายไฟล์ (Separating Modules into Different Files)

จากตัวอย่างที่ผ่านมาทั้งหมดในบทนี้ เราเขียนประกาศหลายโมดูลรวมกันไว้ภายในไฟล์เดียว แต่ทว่าเมื่อโมดูลต่าง ๆ มีขนาดใหญ่ขึ้น คุณอาจต้องการโยกย้ายคำประกาศนิยามของพวกมันออกไปไว้ในไฟล์แยกต่างหากเพื่อช่วยให้ไล่สายตาและจัดการโค้ดได้สะดวกยิ่งขึ้น

ตัวอย่างเช่น เรามาเริ่มปรับปรุงจากโค้ดในรายการโค้ดที่ 7-17 ที่มีหลายโมดูลจำลองระบบร้านอาหารอยู่ร่วมกัน เราจะทำการดึงเอาโมดูลเหล่านั้นแยกออกไปสร้างเป็นไฟล์ แทนที่จะเขียนนิยามโมดูลทั้งหมดรวมกันอยู่ในไฟล์รากเครต ซึ่งไฟล์รากเครตในกรณีนี้คือ src/lib.rs แต่อย่างไรก็ตาม ขั้นตอนปฏิบัติเหล่านี้จะทำงานได้เหมือนกันกับเครตฐานสองที่มีไฟล์รากเครตเป็น src/main.rs

ขั้นตอนแรกสุด เราจะทำการดึงเอาโมดูล front_of_house ออกไปสร้างเป็นไฟล์ของมันเอง ให้คุณลบโค้ดที่อยู่ภายในวงเล็บปีกกาของโมดูล front_of_house ออกทั้งหมด โดยเหลือทิ้งไว้เพียงการประกาศ mod front_of_house; เท่านั้น ซึ่งจะส่งผลให้ไฟล์ src/lib.rs เหลือเนื้อหาโค้ดดังแสดงในรายการโค้ดที่ 7-21 สังเกตว่าโค้ดจุดนี้จะยังคงคอมไพล์ไม่ผ่านจนกว่าเราจะสร้างไฟล์ src/front_of_house.rs ในรายการโค้ดที่ 7-22 สำเร็จ

mod front_of_house;

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

ถัดไป ให้นำเอาโค้ดส่วนที่เคยอยู่ภายในวงเล็บปีกกานั้นไปจัดเก็บไว้ในไฟล์ใหม่ที่สร้างขึ้นชื่อ src/front_of_house.rs ดังที่แสดงในรายการโค้ดที่ 7-22 ทางคอมไพเลอร์จะทราบดีว่าต้องเข้าไปค้นหาไฟล์นี้เนื่องจากมันพบคำสั่งประกาศโมดูลชื่อ front_of_house ปรากฏอยู่ในรากเครต

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

โปรดจำไว้ว่า คุณจำเป็นต้องโหลดไฟล์เข้ามาประมวลผลผ่านคำสั่งประกาศ mod เพียง ครั้งเดียว เท่านั้นในโครงสร้างต้นไม้โมดูลของคุณ เมื่อคอมไพเลอร์รับรู้แล้วว่าไฟล์ดังกล่าวเป็นส่วนหนึ่งของโปรเจกต์ (และทราบตำแหน่งที่โค้ดประดิษฐานอยู่บนต้นไม้โมดูลตามจุดที่คุณวางคำสั่ง mod ไว้) ไฟล์อื่น ๆ ในโปรเจกต์ของคุณก็ควรใช้วิธีอ้างอิงถึงโค้ดในไฟล์นั้นผ่านเส้นทาง (path) ไปยังจุดที่มันประกาศไว้ ดังที่อภิปรายไปในหัวข้อ“เส้นทางสำหรับอ้างอิงถึงไอเท็มในต้นไม้โมดูล” พูดอีกนัยหนึ่งคือ คำสั่ง mod ไม่ใช่ กระบวนการ “include” ดึงไฟล์ซ้ำซ้อนอย่างที่คุณอาจเคยพบเห็นในบางภาษาโปรแกรมอื่น ๆ

ถัดไป เราจะทำการแยกโมดูล hosting ออกไปเป็นไฟล์เฉพาะตัวของมันเอง ขั้นตอนการทำจะมีความแตกต่างออกไปเล็กน้อย เนื่องจาก hosting เป็นโมดูลย่อยของ front_of_house ไม่ใช่โมดูลรากเครต เราจะนำเอาไฟล์ของ hosting ไปวางไว้ในไดเรกทอรีใหม่ที่สร้างขึ้นโดยตั้งชื่อตามโมดูลหลักที่เป็นบรรพบุรุษของมัน ซึ่งในกรณีนี้คือ src/front_of_house

ในการเริ่มย้าย hosting เราจะแก้ไขไฟล์ src/front_of_house.rs ให้หลงเหลือเพียงคำประกาศโมดูล hosting เท่านั้น:

pub mod hosting;

จากนั้น ให้สร้างไดเรกทอรี src/front_of_house ขึ้นมา และสร้างไฟล์ชื่อ hosting.rs เพื่อเก็บบรรจุเนื้อหาคำจำกัดความต่าง ๆ ของโมดูล hosting ไว้ด้านใน:

pub fn add_to_waitlist() {}

หากเราเลือกนำไฟล์ hosting.rs ไปวางไว้ในไดเรกทอรี src แทน คอมไพเลอร์จะเข้าใจผิดและคาดหวังว่าโค้ดในไฟล์ hosting.rs จะต้องสังกัดโมดูล hosting ที่ประกาศไว้ที่ระดับรากเครตโดยตรง ไม่ใช่ประกาศเป็นลูกของโมดูล front_of_house กฎของคอมไพเลอร์ที่ใช้เลือกตรวจจับความสัมพันธ์ระหว่างไฟล์และโมดูลนี้ จะช่วยให้ไดเรกทอรีและไฟล์ต่าง ๆ ในคอมพิวเตอร์ของคุณจับคู่พ้องตรงตามแผนภาพต้นไม้โมดูลได้ดียิ่งขึ้น

เส้นทางอ้างอิงไฟล์ในรูปแบบทางเลือก (Alternate File Paths)

จนถึงจุดนี้เราได้พูดคุยถึงรูปแบบเส้นทางไฟล์ยอดนิยมที่คอมไพเลอร์ Rust เลือกใช้เป็นหลัก แต่อย่างไรก็ตาม Rust ก็ยังคงสนับสนุนรูปแบบอ้างอิงไฟล์แบบโบราณอยู่เช่นกัน โดยสำหรับโมดูลชื่อ front_of_house ที่ประกาศไว้ที่ระดับรากเครต คอมไพเลอร์จะมองหาไฟล์โค้ดในตำแหน่งเหล่านี้:

  • src/front_of_house.rs (วิธีหลักที่เราศึกษาไป)
  • src/front_of_house/mod.rs (รูปแบบดั้งเดิมที่ระบบยังคงรองรับอยู่)

และสำหรับโมดูลชื่อ hosting ซึ่งเป็นโมดูลย่อยของ front_of_house คอมไพเลอร์จะมองหาไฟล์โค้ดในตำแหน่งเหล่านี้:

  • src/front_of_house/hosting.rs (วิธีหลักที่เราศึกษาไป)
  • src/front_of_house/hosting/mod.rs (รูปแบบดั้งเดิมที่ระบบยังคงรองรับอยู่)

หากคุณเรียกใช้รูปแบบทั้งสองวิธีร่วมกันสำหรับโมดูลเดียวกัน ระบบจะแสดงข้อผิดพลาดในการคอมไพล์ การใช้แบบผสมผสานกันสำหรับโมดูลที่ต่างกันในโปรเจกต์เดียวกันสามารถทำได้ แต่ทว่าอาจสร้างความสับสนให้แก่ผู้ร่วมงานที่เข้ามาศึกษาและนำทางโปรเจกต์ของคุณ

ข้อเสียหลักของแนวทางการสร้างไฟล์ชื่อ mod.rs คือ คุณอาจจะลงเอยด้วยการมีไฟล์ชื่อ mod.rs ซ้ำ ๆ กันจำนวนมากในโปรเจกต์ ซึ่งสร้างความสับสนและชวนมึนงงได้ง่ายยามที่คุณเปิดทำงานพวกมันพร้อมกันในหน้าต่างโปรแกรมแก้ไขโค้ด

เราได้ทำการเคลื่อนย้ายโค้ดของแต่ละโมดูลออกไปไว้ในไฟล์แยกต่างหากเรียบร้อยแล้ว โดยโครงสร้างต้นไม้โมดูลยังคงเหมือนเดิมทุกประการ การเรียกใช้งานฟังก์ชันภายใน eat_at_restaurant จะยังคงทำงานได้ตามปกติโดยไม่ต้องแก้ไขเนื้อความใด ๆ แม้ว่าจุดประกาศนิยามจะกระจายไปอยู่ในไฟล์ที่แตกต่างกัน เทคนิคนี้จะช่วยให้คุณสามารถเคลื่อนย้ายโมดูลต่าง ๆ ไปเขียนไว้ในไฟล์ใหม่ ๆ ได้ยามที่เนื้อหาโค้ดเริ่มขยายใหญ่ขึ้น

โปรดสังเกตว่า บรรทัดคำสั่ง pub use crate::front_of_house::hosting ในไฟล์ src/lib.rs ก็ไม่มีความจำเป็นต้องปรับปรุงแก้ไขอะไร และการใช้ use ก็ไม่ได้มีผลกระทบใด ๆ ต่อกระบวนการเลือกไฟล์ที่จะนำมาคอมไพล์รวมในเครต คีย์เวิร์ด mod มีหน้าที่ทำตามคำประกาศนิยามโมดูล และ Rust จะทำหน้าที่วิ่งไปหาไฟล์ที่มีชื่อเดียวกับชื่อโมดูลนั้นเพื่อดึงเอาโค้ดเข้ามาบรรจุทำงาน

สรุป

ภาษา Rust อนุญาตให้คุณแบ่งแยกแพ็กเกจออกเป็นหลายเครต และแบ่งเครตออกเป็นหลายโมดูล เพื่อให้คุณสามารถอ้างอิงเรียกใช้งานไอเท็มต่าง ๆ ข้ามโมดูลกันได้สะดวก คุณสามารถทำได้โดยระบุเส้นทางสัมบูรณ์หรือสัมพัทธ์ และยังดึงเส้นทางเหล่านั้นเข้าสู่ขอบเขตการทำงานผ่านคำสั่ง use เพื่อให้สามารถใช้ชื่อย่อที่สั้นลงสำหรับการเรียกใช้ซ้ำ ๆ ภายในขอบเขตนั้น โดยปกติแล้วโค้ดในโมดูลจะเป็นแบบส่วนตัวเป็นค่าเริ่มต้น แต่คุณสามารถเปลี่ยนนิยามให้กลายเป็นสาธารณะได้โดยใส่คีย์เวิร์ด pub กำกับไว้

ในบทถัดไป เราจะหันมาศึกษาโครงสร้างข้อมูลแบบคอลเลกชัน (collection data structures) ในไลบรารีมาตรฐาน ซึ่งคุณสามารถหยิบมาใช้งานร่วมกับโค้ดที่เป็นระเบียบเรียบร้อยของคุณได้