ทิ้งโค้ด WIP ไว้เป็นคำเตือนใน Rust
(blog.dureuill.net)- ระหว่างพัฒนา Rust หากต้องคอยจัดการทุกเส้นทางข้อผิดพลาดและปัญหา ownership ให้เรียบร้อยทันที มักทำให้จังหวะการลงมือเขียนสะดุด จึงมีประโยชน์ที่จะสร้าง happy path ให้เสร็จก่อน แล้วติดตามงานที่เหลือด้วย คำเตือน
- แม้ใช้แค่ Rust มาตรฐาน ก็สามารถผสม
unwrap,clone,todo!, คำเตือนพื้นฐานของคอมไพเลอร์ และทริกคอมเมนต์เอกสาร///เพื่อทำเครื่องหมายโค้ดที่ยังไม่เสร็จได้ - วิธีเดิมมีข้อจำกัดคือ
unwrapและcloneไม่สร้างคำเตือน ส่วนtodo!ก็ยังพึ่งพาคำเตือนทางอ้อม และทริก///อาจกลายเป็นข้อผิดพลาดตอนคอมไพล์หรือสร้างเอกสารโดยไม่ตั้งใจ ขึ้นอยู่กับตำแหน่งที่ใช้ - crate
wipมีwip!,unwrap_wip,clone_wip,fixme!สำหรับทำให้การ implement ชั่วคราว,unwrap/cloneชั่วคราว, และโน้ตที่ต้องกลับมาแก้ในภายหลัง ปรากฏเป็น คำเตือนเสมอ - Meilisearch ใช้
-D warningใน CI เพื่อเปลี่ยนคำเตือนให้เป็นข้อผิดพลาด ป้องกันไม่ให้โค้ด WIP หลุดเข้ามา และลบ dependencywipที่เพิ่มไว้ระหว่างพัฒนาออกในขั้นตอนเก็บประวัติให้เรียบร้อยก่อนรีวิว
ทำไมระหว่างพัฒนา Rust ถึงต้องการคำเตือน
- Rust เป็นภาษาที่ให้ความสำคัญกับ ความถูกต้อง แต่ถ้าต้องจัดการทุกเส้นทางข้อผิดพลาดให้ครบตั้งแต่ระหว่างพัฒนา อาจทำให้ความเร็วและสมาธิในการ implement ลดลง
- เมื่อ
rustcบล็อกการคอมไพล์เพราะเรื่องอย่างการคืนค่าResult, การจัดการข้อผิดพลาด, หรือการทำ edge case ให้ครบ มักจะมีสองทางเลือก- ลงมือเขียนการจัดการข้อผิดพลาดแบบสมบูรณ์ทันที แล้วรีแฟกเตอร์หลายรอบกว่าจะพร้อมเปิด PR
- ทิ้งเครื่องหมายแบบ
TODO laterไว้ แล้วลดปัญหานั้นลงมาอยู่ในระดับ คำเตือน เพื่อทำงานปัจจุบันต่อได้
- คำเตือนช่วยไม่ให้การพัฒนาถูกหยุด แต่ยังคงเป็นสัญญาณว่าต้องเก็บงานให้เรียบร้อยก่อน merge PR
- Meilisearch รันคอมไพเลอร์ใน CI ด้วยแฟลก
-D warningเพื่อเปลี่ยนคำเตือนทั้งหมดเป็นข้อผิดพลาด ไม่ให้โค้ด WIP ถูกปล่อยออกไปโดยไม่ตั้งใจ - การรีวิวโค้ดเป็นแนวปฏิบัติพื้นฐานที่ช่วยกันบั๊กและแชร์ความรู้ในทีม แต่ถ้าพึ่งแค่ความจำและความระมัดระวังของคน สุดท้ายก็ยังพลาดได้ จึงต้องมี ราวกันตกเพิ่มเติม
วิธีเลื่อนการเก็บงานโค้ดที่ยังไม่เสร็จด้วย Rust มาตรฐาน
-
เลื่อนการจัดการข้อผิดพลาดด้วย
unwrap- ถ้าใช้
unwrapกับResultหรือOptionก็สามารถดึงค่าภายในออกมาและเขียนส่วนที่เหลือต่อได้ โดยแลกกับการ panic ในเส้นทางที่เกิดข้อผิดพลาด - เมื่อส่วนอื่นของโค้ดพร้อมแล้ว ก็สามารถย้อนกลับมาเลือกเอา
unwrapที่ใส่ไว้ชั่วคราวออก แล้วแทนด้วยการจัดการข้อผิดพลาดที่เหมาะสม - แนวทางนี้ตั้งอยู่บนสมมติฐานว่า แม้ Rust จะมีโครงสร้างสำหรับจัดการข้อผิดพลาดอย่างถูกต้อง แต่ก็ไม่จำเป็นต้องทำให้เสร็จ ทันที
- ถ้าใช้
-
เลื่อนปัญหา ownership ด้วย
clone- ปัญหา ownership พบไม่บ่อยเท่าการจัดการข้อผิดพลาด แต่การทำ ownership ให้ถูกต้องอาจต้องเปลี่ยนดีไซน์ด้วย
- ในกรณีนี้ แทนที่จะย้ายค่า ก็สามารถ
cloneเพื่อหลบปัญหา lifetime ไปชั่วคราวได้ - ภายหลังสามารถอาศัยประสบการณ์ระบุได้ว่ากรณีไหนควรส่งตัวแปรที่เหมาะสมขึ้นไปใน stack ระดับบนกว่า เพื่อตัด
cloneออก - แต่เพราะการแก้อาจนำไปสู่การเปลี่ยนโครงสร้างโค้ด การเลื่อนไว้จึงยากกว่าการเลื่อนเรื่องจัดการข้อผิดพลาด
-
ใช้
todo!()เพื่อจองตำแหน่ง implementation ไว้ก่อน- มาโคร
todo!()ในไลบรารีมาตรฐาน Rust ใช้แสดงว่าโค้ดยังไม่เสร็จ และเป็น placeholder ที่มีประโยชน์ตอนทำ prototype เพื่อให้ผ่านการวิเคราะห์ชนิดข้อมูล - เพราะ
todo!()diverge ด้วยการ panic จึงสามารถใช้เป็น expression placeholder ได้ในตำแหน่งชนิดข้อมูลส่วนใหญ่ - ทำให้สามารถกำหนดอินเทอร์เฟซและเรียกใช้ฟังก์ชันได้ก่อน โดยเลื่อน implementation จริงออกไปทีหลัง
todo!()ไม่ได้มีไว้เพื่อบอกผู้ใช้โปรแกรมว่าฟีเจอร์ยังไม่ถูก implement แต่ใกล้เคียงกับ เครื่องมืออำนวยความสะดวกสำหรับ WIP ของคนเขียนและคนรีวิวโค้ดมากกว่า- หากต้องการสื่อกับผู้ใช้ว่า functionality ยังไม่ถูก implement มีมาโคร
unimplemented!()แยกต่างหาก
- มาโคร
-
คำเตือนพื้นฐานของคอมไพเลอร์และทริกคอมเมนต์เอกสาร
- คำเตือนพื้นฐานจากคอมไพเลอร์ Rust ก็มีประโยชน์ในการเผยให้เห็นสถานะที่ยังไม่เสร็จระหว่างพัฒนา
- binding ที่ไม่ได้ใช้งาน
mutที่ไม่จำเป็นResultและOptionที่ไม่ได้ใช้งาน
- หากระหว่างพัฒนาไปซ่อนคำเตือนเหล่านี้ ชั่วคราว ด้วย prefix
_หรือทริกอื่น โค้ดที่ยังไม่เสร็จก็อาจหลุดผ่านไปได้ - สำหรับ edge case หรือ logic ที่เสียแต่ระบบชนิดข้อมูลตามไม่ถึง บางครั้งก็ต้องทิ้งไว้เป็นคอมเมนต์
- ถ้าใส่คอมเมนต์ให้ดูเหมือนคอมเมนต์เอกสาร
///Rust จะสร้างคำเตือนเมื่อมี doc comment ที่ไม่มีสิ่งให้เอกสารกำกับ จึงใช้ติดตามปัญหาตรงจุดนั้นผ่านคำเตือนได้
- คำเตือนพื้นฐานจากคอมไพเลอร์ Rust ก็มีประโยชน์ในการเผยให้เห็นสถานะที่ยังไม่เสร็จระหว่างพัฒนา
ข้อจำกัดของวิธีมาตรฐาน
- ปัญหาใหญ่ที่สุดคือทุกเทคนิคไม่ได้สร้างคำเตือนที่เชื่อมกับปัญหาที่ต้องแก้อย่างสม่ำเสมอ
todo!()มักทำให้เกิดคำเตือน แต่บ่อยครั้งเป็นผลจาก สาเหตุทางอ้อม เช่น อาร์กิวเมนต์ของฟังก์ชันไม่ได้ถูกใช้ หรือ mutation ที่ควรเกิดไม่เกิดขึ้นunwrapและcloneไม่สร้างคำเตือนเลย จึงต้องอาศัยการกลับมาไล่อ่านอีกครั้งก่อน PR สุดท้ายเพื่อเอาออกunwrapอาจคงอยู่ใน production ได้อย่างถูกต้อง ทำให้การใช้แบบชั่วคราวเพื่อ WIP กับการใช้จริงปะปนกันได้- ทริก doc comment
///ค่อนข้างใกล้เคียงการแฮ็ก และในตำแหน่งของ expression นั้นห้ามใช้ doc comment จึงอาจกลายเป็นข้อผิดพลาดตอนคอมไพล์ - หากใส่
///ในตำแหน่งที่เป็นเอกสารได้จริง ก็อาจไม่เกิดคำเตือนตามที่หวัง และทิ้งเอกสารที่ไม่ต้องการไว้ในโค้ดที่ปล่อยใช้งาน
เครื่องมือที่ crate wip มีให้
- crate
wipเป็นชุดเครื่องมือที่ช่วยให้กลยุทธ์ “ทิ้งคำเตือนไว้จัดการทีหลัง” ระหว่างพัฒนา Rust ใช้งานได้สะดวกขึ้น - เพิ่มเข้าโปรเจกต์ได้เหมือน crate ทั่วไป
cargo add wip
- ในไฟล์ที่ต้องใช้ สามารถ import prelude ได้
use wip::prelude::*;
wipอาศัย deprecation warning ของ Rust ดังนั้นถ้า editor ขีดฆ่าการเรียกใช้ฟังก์ชัน deprecated อัตโนมัติ การปิดฟีเจอร์นี้อาจช่วยให้รบกวนน้อยลง-
wip::wip!- มาโคร
wip!ทำงานคล้ายtodo!แต่จะสร้าง คำเตือน ทุกครั้งที่ใช้ - ใช้แทน
todo!ในตำแหน่งที่ต้องการทำเครื่องหมายโค้ดยังไม่เสร็จได้ - ในเวอร์ชันแรกใช้ชื่อ
todoแต่เมื่อ dependency พยายาม shadow มาโครจากstdRust ไม่ค่อยชอบแนวทางนี้ ทำให้เกิดปัญหากับการใช้ prelude
- มาโคร
-
unwrap_wipและclone_wipunwrap_wipและclone_wipถูก implement เป็น extension trait สำหรับResultและOption- เมื่ออยู่ใน scope ก็ทำงานเหมือน
unwrap,cloneปกติ แต่จะสร้างคำเตือนเมื่อใช้งาน - ทำให้สื่อเจตนาได้ชัดว่าไม่ได้ตั้งใจปล่อย
unwrapหรือcloneเหล่านี้ไว้ในโค้ดสุดท้าย
-
wip::fixme!- มาโคร
fixme!เป็นเวอร์ชัน non-panicking ของwip!และไม่ได้ใช้แทน expression - ใช้ได้คล้ายคอมเมนต์ TODO แต่สร้างคำเตือนได้อย่างเสถียรโดยไม่ต้องพึ่งทริก
/// - มักใช้ทิ้งโน้ตว่ามีจุดที่ต้องกลับมาแก้ทีหลัง
- มาโคร
ขั้นตอนการใช้งานและข้อควรระวัง
- crate
wipถูกนำไปใช้แล้วใน PR ล่าสุดบางรายการ รวมถึง Meilisearch PR #6484 ซึ่งเป็นหนึ่งใน PR ขนาดใหญ่ - ขั้นตอนทั่วไปคือ ระหว่างพัฒนาให้เพิ่ม dependency
wipเข้าไปใน crate ของ workspace แล้วลบออกในช่วง rewrite history ให้เรียบร้อยก่อนรีวิว wipช่วยให้โฟกัสกับ happy path ได้ โดยไม่ลืมงานที่ต้องกลับมาจัดการภายหลัง- แต่ก็มีข้อเสียอยู่
- ถ้าใส่
fixmeโดยไม่ให้บริบทเพียงพอ เวลากลับมาแก้ทีหลังจะทำได้ยาก - ใน PR ขนาดใหญ่ จำนวนคำเตือนที่
wipสร้างอาจมากจนกลายเป็นภาระ
- ถ้าใส่
- API ปัจจุบันดูได้จาก เอกสาร
wipบน docs.rs
1 ความคิดเห็น
ความคิดเห็นจาก Lobste.rs
พูดตามตรง จากเหตุผลของ
wipมันให้ความรู้สึกว่าเป็นการ ประดิษฐ์ Clippy ขึ้นมาใหม่ เพราะไม่เข้าใจว่าเดิมทีควรใช้เครื่องมือนี้อย่างไรฝั่งผู้พัฒนา toolchain แยกแยะกันมาโดยตลอดว่าอะไรควรเป็นคำเตือนของ
rustcและอะไรควรเป็น lint ของ Clippy แต่wipดูเหมือนเอาความสามารถที่มีอยู่แล้วมาตั้งชื่อใหม่ แล้วฝืนย้าย lint ที่เคยอยู่ในcargo clippyไปไว้ฝั่งcargo checkและcargo buildตัวอย่างเช่น คำอธิบายของ
wip!ก็คือทำให้#![warn(clippy::todo)]แสดงขึ้นมานอกcargo clippyโดยแลกกับการเพิ่ม dependency ใหม่และไวยากรณ์ที่ไม่มาตรฐาน ส่วนunwrap_wipก็คล้ายกับ#![warn(clippy::unwrap_used)]สำหรับผม
.unwrap()ใช้ตอนทำ prototype เลยเปิด#![warn(clippy::unwrap_used)]ไว้ ส่วน.expect("description of invariant")ใช้ในโค้ด production ตรงที่คนอาจใช้.unwrap()แทน.unwrap_wip()ดังนั้นจึงไม่ได้เปิด#![warn(clippy::expect_used)]ตัวอย่างเช่น.expect("parse regex from const")ส่วน
clone_wipกับfixme!ผมไม่ค่อยแน่ใจ และก็ยังไม่เคยคิดมากพอจะตอบได้ว่านักพัฒนาตั้งใจให้จัดการกรณีแบบนั้นอย่างไรอย่างไรก็ดี ประเด็นที่บทความไม่ได้พูดถึง linter ที่มีอยู่แล้วอย่าง Clippy นั้นก็เป็นเรื่องจริง มันเกี่ยวกับวิธีใช้งานส่วนตัวด้วย เมื่อก่อนผมเคยรัน Clippy ตอนเซฟไฟล์ แต่ในโปรเจ็กต์ของเรา มันช้าเกินไปจนแต่ละครั้งระหว่างการเซฟกินเวลาเป็นนาที เลยเลิกทำแบบนั้น ตอนนี้รันแค่ใน CI กับ pre-push hook
ถ้าดึงคำเตือนมาไว้ใน
rustcจะเร็วกว่าเยอะ และทำให้เห็นคำเตือนแบบ inline ระหว่างทำงานวนซ้ำได้ ซึ่งเป็นหัวใจของ workflow นี้การตั้งค่า Clippy อย่างน้อยตอนที่ผมเช็กล่าสุดก็ยังค่อนข้างยุ่งยาก ไม่มีวิธีกำหนด lint ให้ทุก crate ใน workspace พร้อมกันจากในโค้ด ดังนั้นสำหรับ lint ที่อยากห้ามทั้ง workspace ก็ต้องเพิ่ม
-Dแบบชัดเจนใน CI หรือไม่ก็ใส่ global attribute ซ้ำไปในทุก crate#![warn(clippy::todo)]ต้องมีในทุก crate อยู่ดี ดังนั้นสำหรับเรา มันไม่ได้มีข้อดีมากกว่าการเพิ่มหรือลบ crate เฉพาะตอนพัฒนาtodo!ผมคิดว่าโดยพื้นฐานแล้วควรถูกเตือนโดยrustcหรือ Clippy อยู่แล้ว และถ้าเป็นแบบนั้นจริง ก็เห็นด้วยว่าประโยชน์ของwipจะลดลงผมไม่เห็นด้วยกับคำบอกว่า
unwrap_wipก็คือ#![warn(clippy::unwrap_used)]อยู่สองจุด หนึ่งคือในแง่เจตนา การแบ่งว่าunwrapคือ non-production ส่วนexpectคือ production นั้นพบได้บ่อย แต่ไม่ได้เป็นสากล สองคือในโค้ดเบสเดิมมี unwrap อยู่ประมาณ 4600 จุด จะเปลี่ยนทั้งหมดเป็นexpectก็ได้ แต่แบบนั้นก็ต้องเสียเวลาอย่างมากไปกับการใส่ข้อความชั่วคราวตามจุดต่าง ๆ หรือรื้อสร้างเงื่อนไขตั้งต้นอย่าง “mutex นี้ไม่ได้ poisoned” หรือ “อยากส่งต่อความล้มเหลวของงานนี้ไปให้ผู้เรียก”fixme!มีประโยชน์มาก และน่าใส่ไว้ใน standard library ด้วย จริง ๆ แล้วมันเป็นฟีเจอร์ที่ใช้บ่อยที่สุดใน crate นี้ในบทความไม่ได้ลงรายละเอียดพวกนี้ แต่ crate นี้ไม่ได้ตั้งใจเป็นมาตรฐาน มันเป็นไลบรารีเฉพาะทาง เลยมีฟีเจอร์อย่าง
wip_iterด้วย มันช่วยให้ใช้แมโครอย่างtodoในฟังก์ชันimpl Traitที่คืนค่าตำแหน่ง iterator ได้ และwip_futureก็คล้ายกัน ผมเจอบ่อยพอสมควรกับกรณีที่การอนุมานชนิดคืนค่าและtodoทำงานร่วมกันไม่ได้ จนต้องฝืนสร้าง type ขึ้นมา มีคนหนึ่งถึงกับเสนอให้ทำเป็น compile error โดยปริยายในโหมด release ด้วย และเราวางแผนจะดูเรื่องนั้นพร้อมกับไอเดียปรับปรุงอื่น ๆถ้าจะถ่วงดุลเรื่องต้นทุนของ dependency ใหม่กับไวยากรณ์ที่ไม่มาตรฐานสักหน่อย มันเป็น dependency แบบไฟล์เดียวที่ใช้แค่ระหว่างพัฒนา และโดยปกติก็ลบทิ้งตอนเสร็จงาน
ปัญหาของการทำ prototype ด้วย
unwrapคือภายหลังเมื่อต้องใส่การจัดการข้อผิดพลาดให้ถูกต้อง รูปร่างของชนิดคืนค่า อาจเปลี่ยนไป และยิ่งถ้าฟังก์ชันเหล่านั้นถูกใช้ในสไตล์เชิงฟังก์ชัน ก็จะเกิดผลกระทบเป็นลูกโซ่แนะนำให้เริ่มด้วย
Resultโดยใช้ชนิดข้อผิดพลาดอย่างBox<dyn Error>, สตริง หรือanyhowภายหลังจะค่อยเปลี่ยนเป็น enum ข้อผิดพลาดจริงก็ยังได้ แต่การ refactor โค้ดที่หน้าตาเหมือนไม่มีวันล้มเหลวให้กลายเป็นการคืนResultในภายหลังนั้นยากกว่าอีกอย่าง ถ้าใช้ LLM ก็ต้องระวังด้วย LLM เป็นเครื่องจักรจับคู่แพตเทิร์น มันแยกไม่ออกระหว่าง “อันนี้ตั้งใจทำหยาบ ๆ ก่อนแล้วค่อยแก้ทีหลัง” กับ “โปรเจ็กต์นี้ควรจัดการข้อผิดพลาดแบบนี้” เมื่อเวลาผ่านไป มันอาจขยายการตัดสินใจแบบ “เอาแค่ตอนนี้ก่อน” ให้แพร่ไปทั่วได้ ดังนั้นการใส่คอมเมนต์สั้น ๆ ไว้ใน main สักหนึ่งหรือสองจุดเพื่อบอกว่าเมื่อไร/ที่ไหนที่ยอมให้จัดการข้อผิดพลาดแบบหลวม ๆ ได้ ก็น่าจะช่วยได้
ผมกับเอกสารไม่เห็นตรงกันเรื่องการตีความ
todo!()กับunimplemented!()แต่ย่อหน้าถัดไปที่ถูกตัดออกไปก็ช่วยถ่วงดุลจุดยืนนั้นได้ระดับหนึ่งในเอกสารระบุว่า
todo!ใช้สื่อเจตนาว่าจะทำในภายหลัง และข้อความคือ “not yet implemented” ส่วนunimplemented!ไม่ได้ให้คำมั่นแบบนั้น และข้อความคือ “not implemented”ตอนที่
todo!ถูกเพิ่มเข้ามาใน 1.40.0 มันเป็นแค่ชื่อที่สั้นกว่าเท่านั้น และนอกจากชื่อแล้วก็ไม่ได้ต่างจากunimplemented!ต้องรอถึง 1.42.0 จึงค่อยเอาคำว่า “yet” ออกจากข้อความของ
unimplementedเท่าที่ผมจำได้ การกำหนดความหมายแบบนี้ให้แมโครทั้งสองก็ค่อนข้างมีข้อถกเถียงอยู่บ้าง และการปล่อยtodo!ออกมาในฐานะคำย่อของunimplemented!ก็เช่นกันทุกวันนี้
unimplemented!()มักแนะนำให้ใช้ในที่อย่าง implementation ของ trait ที่ด้วยเหตุผลทางเทคนิคแล้วจำเป็นต้องมี method body แต่ไม่ควรถูกเรียกใช้เลย ตัวอย่างในเอกสาร ก็เป็นลักษณะนั้นพอดีระหว่างพัฒนา การเพิ่ม dependency
wipเข้าไปใน crate ของ workspace แล้วค่อย rewrite history เพื่อลบออกก่อนรีวิว ดูเหมือนน่าจะทำให้ลื่นขึ้นได้อีกด้วยเครื่องมือเสริมบางอย่างการต้อง เขียนไฟล์ล็อกใหม่ ระหว่างแก้ไข PR อยู่กลางทางนั้นน่าลังเลอยู่บ้าง ถ้าระหว่างนั้น dependency tree เปลี่ยนไปที่อื่นด้วย ก็อาจเกิด conflict ที่ไม่จำเป็นได้ อาจจะใช้
jj fixแบบจัดหนักได้เหมือนกัน ไม่ว่าอย่างไร ไอเดียของโปรเจ็กต์นี้ดีมาก และผมคิดว่าสักวันคงได้ใช้ที่ไหนสักแห่งแมโครสำหรับจดโน้ตแบบไม่ panic อย่าง
wip::fixme!เป็นไอเดียที่ดีมากจริง ๆ และ ลูกเล่นด้วย doc comment ก็เป็นวิธีที่เนี้ยบดีและควรจำไว้มีหลายไอเดียที่น่าสนใจและหวังว่าจะต่อยอดไปสู่การเปลี่ยนแปลงฝั่ง
rustcได้todo!()ควรมี คำเตือนโดยปริยาย จริง ๆจะดียิ่งขึ้นถ้านักพัฒนาสามารถปิดคำเตือนพวกนี้ได้ตอนอยู่ในโหมดแฮ็ก เมื่อคำเตือนมีมากเกินไปมันชวนให้ท่วมท้น และปกติผมก็รัน
cargoในคอนโซล 19 บรรทัดด้านล่าง IDE ระหว่างพัฒนา คำเตือนมักไปกลบทั้งข้อผิดพลาดจริงและคำเตือนที่สำคัญกว่า แค่มีวินัยให้เรียงข้อผิดพลาดไว้ท้ายสุดเสมอก็น่าจะช่วยบรรเทาปัญหานี้ได้อีกอย่าง ชื่อ
.unwrap_wip()กับ.clone_wip()ยาวเกินไปเมื่อเทียบกับชื่อเดิม ขอเสนอชื่ออย่าง.du()และ.dc()เป็นตัวย่อของ “dev unwrap” กับ “dev clone” น่าจะใช้ง่ายกว่าเวลาต้องเขียนโค้ดเร็ว ๆจะมีทาง เติม
.clone()กับ.unwrap()ให้อัตโนมัติ ในจุดที่จำเป็นได้ไหม? เช่น ด้วยแฟลกหรือฟีเจอร์อย่าง#[wip(autoclone,autounwrap)]อะไรทำนองนั้น อาจทำไม่ได้ แต่ฝันได้ทุกครั้งที่เริ่มโปรเจ็กต์ใหม่หรือ prototype ใหม่ ผมจะใส่บรรทัดพวกนี้ไว้ใน
main.rs:#![allow(dead_code)]#![allow(unused_variables)]#![allow(unreachable_code)]มันช่วยลดเสียงรบกวนได้มากในช่วงเริ่มต้น