كيفية عرض نوع المتغير في Rust: 3 طرق من المبتدئ إلى المحترف

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

كيفية عرض نوع المتغير في Rust دون عناء

\n\n

هل سبق لك أن كتبت كودًا بلغة Rust وتوقفت فجأة لتتساءل: "ما هو نوع هذا المتغير؟" إذا كان الأمر كذلك، فأنت لست وحدك. سؤال "How do I print the type of a variable in Rust" على Stack Overflow حصد آلاف المشاهدات، وهذا ليس من قبيل الصدفة. Rust هي لغة ذات نظام أنواع صارم ولكن ذكي، وأحيانًا يعرف المترجم أكثر مما نعرف.

\n\n

في هذه المقالة، سنستعرض ثلاث طرق لعرض نوع المتغير: من أسلوب التصحيح البسيط إلى الماكرو الاحترافي. ستتعرف متى تستخدم كل طريقة، وستتوقف عن التخمين بشأن ما يختبئ وراء let x = ....

\n\n

لماذا لا تتيح Rust طباعة النوع ببساطة؟

\n\n

في لغات مثل Python أو JavaScript، يمكنك كتابة print(type(x)) - وتنتهي. في Rust، لا توجد هذه الرفاهية لسببين:

\n\n
    \n
  • الكتابة الثابتة: الأنواع معروفة في مرحلة التجميع، ولكنها ليست دائمًا واضحة للمبرمج (خاصة عند العمل مع closures أو generics المعقدة).
  • \n
  • غياب الانعكاس: لا تخزن Rust معلومات عن الأنواع في وقت التشغيل افتراضيًا - لأن ذلك سيؤثر على الأداء.
  • \n
\n\n

لكن مطوري Rust تركوا لنا بعض الثغرات. دعنا نلقي نظرة عليها.

\n\n

الطريقة 1: std::any::type_name - الأسهل (ولكن ليس مثاليًا)

\n\n

توفر المكتبة القياسية لـ Rust الدالة std::any::type_name. تقوم بإرجاع سلسلة نصية تحتوي على اسم النوع. إليك كيفية عملها:

\n\n
use std::any::type_name;\n\nfn print_type<T>(_: &T) {\n    println!("{}", type_name::<T>());\n}\n\nfn main() {\n    let x = 42;\n    let y = "hello";\n    let z = vec![1, 2, 3];\n    \n    print_type(&x); // i32\n    print_type(&y); // &str\n    print_type(&z); // Vec<i32>\n}
\n\n

الإيجابيات:

\n
    \n
  • أقل كود - يعمل فورًا.
  • \n
  • مناسب للتصحيح والفحوصات السريعة.
  • \n
\n\n

السلبيات:

\n
    \n
  • النتيجة تعتمد على المترجم: في إصدارات مختلفة من Rust أو على منصات مختلفة، قد تختلف السلسلة النصية.
  • \n
  • لا يمكن استخدامه في منطق الإنتاج حيث تحتاج إلى اتخاذ قرارات بناءً على النوع.
  • \n
\n\n

مثال عملي: تصحيح تعبير معقد

\n\n

تخيل أنك تعمل مع المكررات (iterators) وسلاسل الدوال. ما هو النوع النهائي؟

\n\n
use std::any::type_name;\n\nfn main() {\n    let data = vec![1, 2, 3, 4, 5];\n    let result = data.iter()\n        .filter(|x| *x % 2 == 0)\n        .map(|x| x * 2)\n        .collect::<Vec<_>>();\n    \n    // هل نريد معرفة النوع قبل collect؟\n    let intermediate = data.iter()\n        .filter(|x| *x % 2 == 0)\n        .map(|x| x * 2);\n    \n    println!("نوع intermediate: {}", type_name::<decltype!(intermediate)>()); \n    // عفوًا، هذا لن يعمل - يجب تمرير مرجع\n    println!("نوع intermediate: {}", type_name_of_val(&intermediate));\n}\n\nfn type_name_of_val<T>(_: &T) -> &str {\n    type_name::<T>()\n}
\n\n

هذه الحيلة ستنقذك عندما يصدر المترجم خطأً ولا تفهم ما هو النوع الذي يتوقعه.

\n\n

الطريقة 2: استخدام أخطاء المترجم - حيلة ذكية

\n\n

أحيانًا أفضل طريقة لمعرفة النوع هي إجبار المترجم على إخبارك به. هناك حيلة قديمة ولكنها فعالة: تعيين المتغير إلى نوع غير موجود.

\n\n
fn main() {\n    let x = 42;\n    let y: () = x; // خطأ! expected (), found i32\n}
\n\n

سيصدر المترجم شيئًا مثل:

\n
error[E0308]: mismatched types\n --> src/main.rs:3:18\n  |\n3 |     let y: () = x;\n  |            --   ^ expected `()`, found `i32`\n  |            |\n  |            expected due to this
\n\n

الإيجابيات:

\n
    \n
  • لا يتطلب استيرادات.
  • \n
  • يعمل حتى في البيئات بدون std (no_std).
  • \n

المدونات

توصيات الكتب