Builder ที่มีสถานะตามชนิดข้อมูล
เข้ารหัสความถูกต้องไว้ในระบบชนิดข้อมูล
Builder ที่มีสถานะตามชนิดข้อมูล เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
ปัญหาของ Builder แบบทั่วไป
builder แบบปกติเปิดให้คุณเรียก build() ได้ทุกเมื่อ แม้กระทั่งก่อนตั้งค่าฟิลด์ที่จำเป็นครบ ข้อมูลที่ขาดหายไปจะแสดงออกมาเป็นการหยุดทำงานระหว่างรันไทม์ หรือเป็น Err
builder แบบสถานะชนิดข้อมูลจะเลื่อนการตรวจสอบไปไว้ในเวลาคอมไพล์ การลืมขั้นตอนที่จำเป็นจะทำให้คอมไพล์ไม่ผ่านทันที
การเข้ารหัสสถานะไว้ในชนิดข้อมูล
เคล็ดลับคือทำให้ builder เป็นชนิดทั่วไปเหนือชนิดเครื่องหมาย ซึ่งแทนขั้นตอนที่ทำเสร็จแล้ว เมื่อคุณตั้งค่าฟิลด์แต่ละรายการ ชนิดของ builder จะเปลี่ยนไป
เมื่อตัวบ่งชี้ที่จำเป็นทุกตัวมีสถานะ "set" แล้วเท่านั้น เมธอด build() จึงจะพร้อมใช้งาน
struct Missing;
struct Set;Builder ที่เป็นชนิดทั่วไปเหนือเครื่องหมาย
กำหนดพารามิเตอร์ชนิดข้อมูลให้ builder สำหรับสถานะของฟิลด์ที่จำเป็นแต่ละรายการ PhantomData จะเก็บเครื่องหมายไว้โดยไม่จัดเก็บข้อมูลจริง
use std::marker::PhantomData;
struct ReqBuilder<H, U> {
url: Option<String>,
method: Option<String>,
_state: PhantomData<(H, U)>,
}สถานะเริ่มต้น
ตัวสร้างจะคืนค่า builder ที่เครื่องหมายที่จำเป็นทุกตัวเป็น Missing ในจุดนี้ยังไม่มี build() อยู่ ดังนั้นระบบชนิดข้อมูลจึงทราบว่าวัตถุนี้ยังไม่สมบูรณ์
impl ReqBuilder<Missing, Missing> {
fn new() -> Self {
ReqBuilder { url: None, method: None, _state: PhantomData }
}
}การเปลี่ยนสถานะเครื่องหมาย
เมธอดตั้งค่าจะใช้ builder เดิมไป และคืนค่า builder ใหม่ที่เครื่องหมายของฟิลด์นั้นเปลี่ยนเป็น Set ส่วนเครื่องหมายอื่นยังคงเดิม โดยเก็บพารามิเตอร์ชนิดข้อมูลของมันไว้เป็นชนิดทั่วไป
impl<U> ReqBuilder<Missing, U> {
fn url(self, url: &str) -> ReqBuilder<Set, U> {
ReqBuilder { url: Some(url.to_string()),
method: self.method, _state: PhantomData }
}
}การเปลี่ยนสถานะครั้งที่สอง
การตั้งค่าเมธอดทำงานในลักษณะเดียวกัน โดยเปลี่ยนเครื่องหมายตัวที่สองจาก Missing เป็น Set และไม่แตะต้องตัวแรก
impl<H> ReqBuilder<H, Missing> {
fn method(self, m: &str) -> ReqBuilder<H, Set> {
ReqBuilder { url: self.url,
method: Some(m.to_string()), _state: PhantomData }
}
}มี build() ได้เมื่อกำหนดค่าครบเท่านั้น
สิ่งสำคัญคือ build() ถูก implement เฉพาะสำหรับ ReqBuilder<Set, Set> เท่านั้น สถานะอื่นจะไม่มีเมธอดนี้อยู่ ดังนั้นการเรียกใช้จะคอมไพล์ไม่ผ่าน
ภายในนั้น การเรียก unwrap จะไม่สามารถทำให้โปรแกรมหยุดทำงานได้ เพราะชนิดข้อมูลยืนยันว่ามีฟิลด์ทั้งสองอยู่แล้ว
struct Request { url: String, method: String }
impl ReqBuilder<Set, Set> {
fn build(self) -> Request {
Request { url: self.url.unwrap(), method: self.method.unwrap() }
}
}นำทุกอย่างมาประกอบกัน
ลำดับการเรียกที่ถูกต้องจะคอมไพล์ได้อย่างราบรื่น เพราะแต่ละการเรียกจะทำให้ builder เข้าใกล้ ReqBuilder<Set, Set> ซึ่งมี build() อยู่
fn demo() -> Request {
ReqBuilder::new()
.url("https://example.com")
.method("GET")
.build()
}ข้อผิดพลาดขณะคอมไพล์ที่คุณต้องการ
หากข้ามขั้นตอนที่จำเป็น คอมไพเลอร์จะปฏิเสธ การเรียก build() บน ReqBuilder<Set, Missing> จะแจ้งว่า "no method named build" ทำให้ตรวจพบสิ่งที่ขาดก่อนที่โปรแกรมจะเริ่มทำงาน
// ReqBuilder::new().url("x").build();
// error: no method named `build` found for
// ReqBuilder<Set, Missing>เวอร์ชันที่เรียกใช้งานได้ในตัวเอง
โปรแกรมขนาดเล็กนี้ใช้ฟิลด์ที่จำเป็นเพียงรายการเดียว เพื่อแสดงการเปลี่ยนสถานะทั้งหมดตั้งแต่ต้นจนจบ โปรแกรมคอมไพล์ได้และแสดงค่าที่สร้างขึ้น
use std::marker::PhantomData;
struct Missing; struct Set;
struct B<N> { name: Option<String>, _s: PhantomData<N> }
impl B<Missing> {
fn new() -> Self { B { name: None, _s: PhantomData } }
fn name(self, n: &str) -> B<Set> {
B { name: Some(n.to_string()), _s: PhantomData }
}
}
impl B<Set> {
fn build(self) -> String { self.name.unwrap() }
}
fn main() {
let v = B::new().name("prod").build();
println!("built: {}", v);
}ต้นทุนและข้อแลกเปลี่ยน
builder แบบสถานะชนิดข้อมูลให้การรับประกันตั้งแต่เวลาคอมไพล์โดยไม่มีต้นทุนขณะทำงาน เนื่องจากเครื่องหมายจะถูกลบออก ต้นทุนที่ต้องแลกคือกลไกชนิดข้อมูลที่มากขึ้น และบล็อก impl ที่เพิ่มขึ้นแบบผสมผสานเมื่อมีฟิลด์ที่จำเป็นมากขึ้น
ควรใช้รูปแบบนี้กับ API ที่การสร้างแบบไม่สมบูรณ์ต้องเป็นไปไม่ได้ตั้งแต่การออกแบบ
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ระบุว่าสิ่งใดเป็นตัวบังคับให้ builder แบบสถานะชนิดข้อมูลต้องสมบูรณ์
สรุปทบทวน
builder แบบสถานะชนิดข้อมูลเข้ารหัสแต่ละขั้นตอนที่จำเป็นไว้เป็นพารามิเตอร์ชนิดข้อมูลแบบเครื่องหมาย ซึ่งเก็บไว้ด้วย PhantomData เมธอดตั้งค่าจะใช้ builder ไป และคืนค่าชนิดข้อมูลใหม่ที่เครื่องหมายหนึ่งตัวเปลี่ยนเป็น Set
เนื่องจาก build() ถูก implement เฉพาะสำหรับสถานะที่เป็น Set ทั้งหมด การลืมขั้นตอนจึงเป็นข้อผิดพลาดขณะคอมไพล์ ไม่ใช่การหยุดทำงานระหว่างรันไทม์ และไม่มีต้นทุนขณะทำงาน
คำถามที่พบบ่อย
บทเรียน “Builder ที่มีสถานะตามชนิดข้อมูล” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “Builder ที่มีสถานะตามชนิดข้อมูล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “Builder ที่มีสถานะตามชนิดข้อมูล”
เข้ารหัสความถูกต้องไว้ในระบบชนิดข้อมูล คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “Builder ที่มีสถานะตามชนิดข้อมูล” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม
ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- รูปแบบ Builder
- รูปแบบ Newtype
- Builder ที่มีสถานะตามชนิดข้อมูล
- ความสะดวกของ Deref และตัวห่อหุ้ม