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

การเริ่มต้นเอกสารประกอบอย่างง่าย

คำอธิบาย

หาก struct ของคุณมีขั้นตอนการกำหนดค่าเริ่มต้น (Initialization) ที่ซับซ้อนหรือต้องเขียนโค้ดยืดยาวเมื่อเขียนตัวอย่างในเอกสารประกอบ (Rustdoc) การห่อโค้ดตัวอย่างนั้นไว้ในฟังก์ชันตัวช่วยที่ประกาศรับ struct เป็นอาร์กิวเมนต์ จะช่วยประหยัดเวลาและลดโค้ดซ้ำซ้อนได้อย่างมาก

ที่มาและเหตุผล

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

ตัวอย่างเช่น:

struct Connection {
    name: String,
    stream: TcpStream,
}

impl Connection {
    /// Sends a request over the connection.
    ///
    /// # Example
    /// ```no_run
    /// # // Boilerplate are required to get an example working.
    /// # let stream = TcpStream::connect("127.0.0.1:34254");
    /// # let connection = Connection { name: "foo".to_owned(), stream };
    /// # let request = Request::new("RequestId", RequestType::Get, "payload");
    /// let response = connection.send_request(request);
    /// assert!(response.is_ok());
    /// ```
    fn send_request(&self, request: Request) -> Result<Status, SendErr> {
        // ...
    }

    /// Oh no, all that boilerplate needs to be repeated here!
    fn check_status(&self) -> Status {
        // ...
    }
}

ตัวอย่าง

แทนที่จะต้องพิมพ์โค้ด boilerplate ทั้งหมดซ้ำๆ เพื่อสร้าง Connection และ Request การสร้างฟังก์ชันตัวช่วยห่อหุ้มที่รับค่าเหล่านั้นเข้ามาเป็นพารามิเตอร์จะทำได้ง่ายและสะอาดกว่ามาก:

struct Connection {
    name: String,
    stream: TcpStream,
}

impl Connection {
    /// Sends a request over the connection.
    ///
    /// # Example
    /// ```
    /// # fn call_send(connection: Connection, request: Request) {
    /// let response = connection.send_request(request);
    /// assert!(response.is_ok());
    /// # }
    /// ```
    fn send_request(&self, request: Request) -> Result<Status, SendErr> {
        // ...
    }
}

ข้อสังเกต: ในตัวอย่างข้างต้น บรรทัด assert!(response.is_ok()); จะไม่ได้ถูกประมวลผลจริงในระหว่างการรันเทสต์ เพราะมันบรรจุอยู่ภายในฟังก์ชันที่ไม่มีการเรียกใช้งาน

ข้อดี

ช่วยให้เอกสารประกอบมีความกระชับ สะอาดตา และหลีกเลี่ยงการเขียนโค้ด boilerplate ซ้ำซากจำเจในทุกๆ เมท็อด

ข้อเสีย

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

เจาะลึกรายละเอียด

หากโค้ดตัวอย่างไม่จำเป็นต้องมีการรัน assertion จริงๆ แพตเทิร์นนี้ถือว่าตอบโจทย์ได้เป็นอย่างดี

แต่หากต้องการให้มีการทดสอบ assertion จริง ทางเลือกหนึ่งคือการสร้างเมท็อดแบบ public เพื่อทำหน้าที่สร้างอินสแตนซ์ทดสอบขึ้นมา แล้วกำกับด้วยแอตทริบิวต์ #[doc(hidden)] ไว้ (เพื่อซ่อนไม่ให้ผู้ใช้ทั่วไปมองเห็นในหน้าเอกสาร) เมื่อทำเช่นนี้ โค้ดตัวอย่างใน rustdoc ก็จะสามารถเรียกใช้เมท็อดดังกล่าวเพื่อสร้างออบเจกต์ขึ้นมาทดสอบได้อย่างสะดวก