نمط Newtype
جزء من قسم Object Oriented Programming في رحلة Rust على Coddy — الدرس 54 من 61.
أحيانًا يكون لديك قيمتان من نفس النوع تمثلان أشياء مختلفة تمامًا. قد يكون كل من البريد الإلكتروني للمستخدم وكلمة مروره قيمًا من النوع String، لكن تبديلهما عن طريق الخطأ في استدعاء دالة سيكون خطأً برمجياً خطيراً. يعالج نمط Newtype هذا الأمر من خلال إنشاء أنواع متميزة تغلف الأنواع الأولية (primitives).
النوع الجديد (newtype) هو ببساطة هيكل صف (tuple struct) يحتوي على حقل واحد فقط:
struct Password(String);
struct Email(String);
على الرغم من أن كليهما يغلفان String، إلا أن المترجم يعاملهما كأنواع مختلفة تماماً. لا يمكنك تمرير Password عن طريق الخطأ حيث يُتوقع Email—سيكتشف المترجم الخطأ.
fn send_reset_email(email: Email, password: Password) {
// ...
}
let email = Email(String::from("user@example.com"));
let pass = Password(String::from("secret123"));
send_reset_email(email, pass); // الترتيب الصحيح مفروض بواسطة الأنواع
// send_reset_email(pass, email); // لن يتم التجميع!
للوصول إلى القيمة الداخلية، استخدم .0 بما أنه tuple struct:
let pass = Password(String::from("secret123"));
println!("Length: {}", pass.0.len());
يمكنك أيضًا إضافة دوال (methods) إلى الـ newtype الخاص بك، مما يمنحه سلوكًا خاصًا بما يمثله — مثل التحقق من الصحة أو التنسيق — وهو ما لن يكون منطقيًا في String عادي.
التحدي
سهللنقم ببناء نظام تسجيل مستخدمين آمن من حيث الأنواع (type-safe) باستخدام نمط Newtype! ستقوم بإنشاء أنواع متميزة لـ Username و UserId لمنع الخلط بينهما عن طريق الخطأ - على الرغم من أن كليهما يغلفان قيمًا بسيطة.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
user_types.rs: قم بتعريف هيكلين (structs) عامين من نوع newtype -UsernameيغلفStringوUserIdيغلفu32. أضف دالة (method) تسمىvalueلكل منهما تعيد مرجعًا (reference) للبيانات الداخلية (بالنسبة لـ Username، أعد&String؛ وبالنسبة لـ UserId، أعد&u32). يوفر هذا وصولاً محكومًا للقيم المغلفة.main.rs: قم باستيراد وحدة user_types الخاصة بك وأنشئ دالة تسمىdisplay_userتأخذUserIdكمعامل أول وUsernameكمعامل ثانٍ. يجب أن تقوم الدالة بطباعة معلومات المستخدم. ثم قم بإنشاء مثيلات (instances) من كلا النوعين باستخدام المدخلات المقدمة واستدعِ الدالة الخاصة بك.
الفكرة الأساسية هنا هي أنه على الرغم من أن UserId و Username هما مجرد مغلفات بسيطة، إلا أن المترجم (compiler) يعاملهما كأنواع مختلفة تمامًا. لا يمكنك تمرير اسم مستخدم عن طريق الخطأ حيث يُتوقع معرف (ID)!
يجب أن تقوم دالة display_user بالطباعة بهذا التنسيق:
User #{id}: {username}على سبيل المثال، مع المدخلات 42 و alice_dev:
User #42: alice_devومع المدخلات 1001 و bob_smith:
User #1001: bob_smithستتلقى مدخلين: معرف المستخدم (قم بتحليله كـ u32) وسلسلة اسم المستخدم.
جرّب بنفسك
mod user_types;
use user_types::{Username, UserId};
// مهمة: قم بإنشاء دالة تسمى `display_user` تأخذ:
// - المعامل الأول: UserId
// - المعامل الثاني: Username
// يجب أن تطبع الدالة: "User #{id}: {username}"
// استخدم طريقة .value() للوصول إلى القيم الداخلية
fn main() {
// قراءة المدخلات
let mut id_input = String::new();
std::io::stdin().read_line(&mut id_input).expect("Failed to read line");
let id: u32 = id_input.trim().parse().expect("Invalid number");
let mut username_input = String::new();
std::io::stdin().read_line(&mut username_input).expect("Failed to read line");
let username = username_input.trim().to_string();
// مهمة: قم بإنشاء مثيلات من UserId و Username باستخدام المدخلات
// مهمة: قم باستدعاء display_user مع الوسائط الصحيحة
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1الأساليب والسلوك
مقدمة في كتل التنفيذمعامل Selfالأساليب القابلة للتعديلالدوال المرتبطةكتل تنفيذ متعددةتسلسل الأساليبمراجعة - إجراءات المستطيل4مشروع: حيوان أليف افتراضي
تعريف الحيوان الأليفإطعام الحيوان الأليف2التغليف والوحدات البرمجية
أساسيات الوحدات البرمجيةالكلمة المفتاحية Publicالحقول الخاصةدوال Gettersدوال Settersمراجعة - الخزانة الآمنة5الأنواع العامة (Generics)
الـ Structs العامةالـ Methods العامةأنواع عامة متعددةالـ Functions العامةمراجعة - Coordinate Point3التعدادات المتقدمة (Advanced Enums)
التعدادات مع البياناتالأساليب (Methods) في التعداداتمطابقة حالات البياناتمراجعة التعداد Optionملخص - التعداد Shape6تعريف الـ Traits
ما هي الـ Trait؟تنفيذ الـ Traitsالتنفيذات الافتراضيةتجاوز التنفيذات الافتراضيةالـ Traits مع المعاملاتملخص - مشغل الوسائط