0Pricing
Learn Rust Coding · บทเรียน

รูปแบบ Newtype

ห่อชนิดข้อมูลเพื่อความปลอดภัยและความชัดเจน

รูปแบบ Newtype เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน

Newtype คืออะไร

newtype คือโครงสร้างแบบทูเพิลที่มีฟิลด์เดียว ใช้ห่อชนิดข้อมูลเดิมเพื่อมอบอัตลักษณ์ที่แตกต่าง struct Meters(f64) เป็นชนิดข้อมูลใหม่โดยสมบูรณ์ แม้จะเก็บ f64 ธรรมดาไว้ก็ตาม

ตัวห่อไม่มีต้นทุนขณะรันไทม์ แต่ช่วยให้คอมไพเลอร์บังคับใช้ความหมายที่ชนิดพื้นฐานดิบไม่สามารถบังคับได้

struct Meters(f64);
struct Seconds(f64);

ป้องกันการสับสนระหว่างหน่วย

ชนิดพื้นฐานดิบทำให้สับสนได้ง่าย หากทั้งระยะทางและเวลาเป็น f64 ก็ไม่มีอะไรป้องกันไม่ให้คุณสลับค่าทั้งสองตอนเรียกใช้

การห่อแต่ละค่าไว้ใน newtype ของตนเองจะทำให้ความผิดพลาดดังกล่าวกลายเป็นข้อผิดพลาดขณะคอมไพล์ แทนที่จะเป็นบั๊กที่เกิดขึ้นโดยไม่รู้ตัว

fn speed(d: Meters, t: Seconds) -> f64 {
    d.0 / t.0
}
// speed(Seconds(2.0), Meters(10.0)) -> compile error

การเข้าถึงค่าภายใน

คุณเข้าถึงค่าที่ถูกห่อได้ผ่านดัชนีทูเพิล .0 นอกจากนี้ newtype จำนวนมากยังเปิดเผยเมธอดหรือใช้งาน From เพื่อให้แปลงค่าได้สะดวก

struct UserId(u64);
impl UserId {
    fn value(&self) -> u64 { self.0 }
}
fn main() {
    let id = UserId(42);
    println!("id = {}", id.value());
}

การห่อหุ้มเงื่อนไขคงที่

ทำให้ฟิลด์ภายในเป็นส่วนตัวและตรวจสอบค่าในคอนสตรักเตอร์ จากนั้นค่าทุกค่าของ newtype จะได้รับการรับประกันว่าถูกต้อง โค้ดส่วนถัดไปจึงไม่ต้องตรวจสอบซ้ำ

pub struct Email(String);
impl Email {
    pub fn new(s: &str) -> Option<Email> {
        if s.contains('@') {
            Some(Email(s.to_string()))
        } else {
            None
        }
    }
}

กฎ Orphan

Rust ห้ามใช้งานเทรตจากภายนอกกับชนิดข้อมูลจากภายนอก คุณไม่สามารถเขียน impl Display for Vec<T> ได้ เพราะคุณไม่ได้เป็นเจ้าของทั้งเทรตและชนิดข้อมูล

กฎนี้ช่วยให้ความสอดคล้องของเทรตระหว่างครेटต่าง ๆ ถูกต้อง แต่ก็อาจขัดขวางการใช้งานที่เป็นประโยชน์บางอย่าง

Newtype หลีกเลี่ยงกฎ Orphan ได้

เนื่องจาก newtype ถูกกำหนดไว้ใน crate ของคุณ คุณจึงเป็นเจ้าของชนิดข้อมูลภายในเครื่องนี้ และสามารถ implement trait ใด ๆ ให้กับมันได้ นี่คือวิธีมาตรฐานในการแก้ปัญหากฎ orphan

use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

Wrapper Display ที่เรียกใช้งานได้

ในที่นี้ Wrapper จากฉากก่อนหน้าถูกนำมาใช้งานจริง เราเป็นเจ้าของ Wrapper ดังนั้นจึงสามารถ implement Display ให้กับมันได้ และโปรแกรมจะแสดงรายการที่นำมาต่อกัน

use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}
fn main() {
    let w = Wrapper(vec!["a".into(), "b".into()]);
    println!("{}", w);
}

การจำกัดขอบเขต API

การห่อชนิดข้อมูลที่มีความสามารถสูงช่วยให้คุณเปิดเผยเฉพาะส่วนย่อยที่ปลอดภัยได้ NonEmptyVec สามารถซ่อนเมธอดที่แก้ไขข้อมูล ซึ่งอาจทำให้มันกลายเป็นค่าว่าง เพื่อรักษาเงื่อนไขคงที่ของมันไว้

pub struct NonEmptyVec<T>(Vec<T>);
impl<T> NonEmptyVec<T> {
    pub fn new(first: T) -> Self {
        NonEmptyVec(vec![first])
    }
    pub fn first(&self) -> &T {
        &self.0[0]
    }
}

นามธรรมที่ไม่มีต้นทุน

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

การเพิ่ม #[repr(transparent)] รับประกันว่ารูปแบบการจัดวางจะเหมือนกันทุกประการ ซึ่งสำคัญต่อ FFI

#[repr(transparent)]
struct Celsius(f64);

การสร้าง Trait บน Newtype โดยอัตโนมัติ

โดยทั่วไป newtype จะสร้าง trait มาตรฐานโดยอัตโนมัติ เพื่อให้ทำงานเหมือนค่าภายในเมื่อเหมาะสม การสร้างโดยอัตโนมัติช่วยให้ใช้งานเป็นคีย์ การเปรียบเทียบ และผลลัพธ์การตรวจแก้จุดบกพร่องได้สะดวก

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct ProductId(u32);
fn main() {
    let a = ProductId(7);
    let b = a.clone();
    println!("{:?} == {:?}: {}", a, b, a == b);
}

Newtype เทียบกับนามแฝงชนิดข้อมูล

อย่าสับสนระหว่าง newtype กับนามแฝงชนิดข้อมูล type Meters = f64 เป็นเพียงชื่อเท่านั้น มันยังคงเป็น f64 และไม่ได้เพิ่มความปลอดภัยใด ๆ

ส่วน newtype struct Meters(f64) เป็นชนิดข้อมูลที่แตกต่างอย่างแท้จริง ซึ่งคอมไพเลอร์สามารถแยกออกจากกันได้

type Km = f64;       // alias: interchangeable with f64
struct Mi(f64);      // newtype: distinct from f64

ตรวจสอบความเข้าใจอย่างรวดเร็ว

พิจารณาว่าเหตุใด newtype จึงช่วยได้ในกรณีที่นามแฝงชนิดข้อมูลช่วยไม่ได้

สรุปทบทวน

รูปแบบ newtype จะห่อชนิดข้อมูลที่มีอยู่แล้วไว้ในทัปเพิลสตรักต์ที่มีฟิลด์เดียว เพื่อให้ได้อัตลักษณ์ที่แตกต่างโดยไม่มีต้นทุนขณะทำงาน ช่วยป้องกันการสับสนระหว่างค่า ห่อหุ้มเงื่อนไขคงที่ไว้เบื้องหลังฟิลด์ส่วนตัว และหลีกเลี่ยงกฎ orphan ทำให้คุณสามารถ implement trait จากภายนอกได้

ต่างจากนามแฝงชนิดข้อมูล newtype เป็นชนิดข้อมูลจริงที่แยกจากกัน

คำถามที่พบบ่อย

บทเรียน “รูปแบบ Newtype” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “รูปแบบ Newtype” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “รูปแบบ Newtype”

ห่อชนิดข้อมูลเพื่อความปลอดภัยและความชัดเจน คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “รูปแบบ Newtype” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม

ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. รูปแบบ Builder
  2. รูปแบบ Newtype
  3. Builder ที่มีสถานะตามชนิดข้อมูล
  4. ความสะดวกของ Deref และตัวห่อหุ้ม
← กลับไปที่ Learn Rust Coding