مقدمة: لماذا تحويل الأرقام في Rust ليس مزحة؟
\nهل سبق لك أن كتبت كوداً بلغة Python وقمت بتحويل int_var = 3.14 ثم float_var = int_var بكل سهولة؟ في Python هذا يعمل لأن الأنواع ديناميكية. لكن في Rust، مثل صانع ساعات دقيق، كل ترس يجب أن يكون بحجمه المناسب. إذا حاولت إدخال u8 (0–255) في u16 فهذا أمر، أما العكس فقد يحدث فقدان صامت للبيانات أو panic.
على Stack Overflow، سؤال «How do I convert between numeric types safely and idiomatically?» حصل على آلاف المشاهدات. وهذا ليس صدفة: المبتدئون غالباً ما يضيعون بين as و From و TryFrom. في هذا المقال، سنشرح جميع طرق تحويل الأرقام في Rust، ومتى تستخدم كل منها، مع أمثلة جاهزة للتطبيق.
1. التحويل البسيط باستخدام as: سريع لكنه خطير
\nأسهل طريقة هي استخدام العامل as. لكنه يأتي بمخاطر خفية.
let big: u32 = 300;\nlet small: u8 = big as u8; // 300 لا يسع في u8!\nprintln!("{}", small); // الناتج: 44 (300 mod 256)\n\nماذا حدث؟ Rust أخذ فقط آخر 8 بتات من الرقم 300 (0b1_0010_1100) وتجاهل الباقي، تاركاً 0b0010_1100 = 44. هذا يسمى الاقتطاع (truncation). بالنسبة لـ u8 قد يكون هذا مفاجئاً.
as آمن فقط عند توسيع النوع (مثل u8 → u16)، عندما تكون القيمة مناسبة تماماً. أما للتصغير فهو قنبلة موقوتة.
متى تستخدم as؟ فقط عندما تكون متأكداً تماماً أن القيمة لن تتجاوز حدود النوع الهدف. مثلاً عند العمل مع أقنعة البتات أو البروتوكولات منخفضة المستوى.
2. الطريقة الاحترافية: Traits From و Into
\nRust لغة تجعل الأخطاء واضحة. لذلك توفر المكتبة القياسية traits From و Into لـ تحويل آمن لا يفشل أبداً.
القاعدة بسيطة: إذا كان التحويل ناجحاً دائماً (بدون فقدان بيانات)، فإن الـ trait مُطبّق. مثال:
\nlet small: u8 = 42;\nlet big: u16 = small.into(); // Into يستدعي From تلقائياً\n// أو\nlet big: u16 = u16::from(small);\nprintln!("{}", big); // 42\n\nملاحظة: From مُطبّق فقط للأزواج حيث التحويل آمن تماماً. مثلاً u8 → u16، i32 → f64، bool → i32. لكن u16 → u8 غير مطبّق لأنه قد يحدث تجاوز.
جرب ترجمة هذا الكود:
\nlet big: u16 = 256;\nlet small: u8 = big.into(); // خطأ في الترجمة!\n\nسيخبرك المترجم: "the trait `From
3. عندما قد يفشل التحويل: TryFrom و TryInto
\nماذا لو أردنا تحويل u16 إلى u8 مع التحقق؟ هنا تأتي TryFrom و TryInto. ترجع هذه Result يجب معالجته.
use std::convert::TryFrom;\n\nlet big: u16 = 255;\nmatch u8::try_from(big) {\n Ok(small) => println!("نجاح: {}", small),\n Err(e) => println!("خطأ: {}", e),\n}\n// الناتج: نجاح: 255\n\nlet big: u16 = 256;\nmatch u8::try_from(big) {\n Ok(small) => println!("نجاح: {}", small),\n Err(e) => println!("خطأ: {}", e),\n}\n// الناتج: خطأ: TryFromIntError(())\n\nهذا هو أسلوب Rust الاحترافي: تعالج الخطأ المحتمل بشكل صريح. يمكنك استخدام unwrap() إذا كنت متأكداً 100%، لكن الأفضل استخدام match.