تحويل الأرقام بأمان في Rust: دليل شامل للمبتدئين

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

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

ابدأ الدورة

مقدمة: لماذا تحويل الأرقام في Rust ليس مزحة؟

\n

هل سبق لك أن كتبت كوداً بلغة Python وقمت بتحويل int_var = 3.14 ثم float_var = int_var بكل سهولة؟ في Python هذا يعمل لأن الأنواع ديناميكية. لكن في Rust، مثل صانع ساعات دقيق، كل ترس يجب أن يكون بحجمه المناسب. إذا حاولت إدخال u8 (0–255) في u16 فهذا أمر، أما العكس فقد يحدث فقدان صامت للبيانات أو panic.

\n

على Stack Overflow، سؤال «How do I convert between numeric types safely and idiomatically?» حصل على آلاف المشاهدات. وهذا ليس صدفة: المبتدئون غالباً ما يضيعون بين as و From و TryFrom. في هذا المقال، سنشرح جميع طرق تحويل الأرقام في Rust، ومتى تستخدم كل منها، مع أمثلة جاهزة للتطبيق.

\n\n

1. التحويل البسيط باستخدام as: سريع لكنه خطير

\n

أسهل طريقة هي استخدام العامل as. لكنه يأتي بمخاطر خفية.

\n
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 قد يكون هذا مفاجئاً.

\n

as آمن فقط عند توسيع النوع (مثل u8 → u16)، عندما تكون القيمة مناسبة تماماً. أما للتصغير فهو قنبلة موقوتة.

\n

متى تستخدم as؟ فقط عندما تكون متأكداً تماماً أن القيمة لن تتجاوز حدود النوع الهدف. مثلاً عند العمل مع أقنعة البتات أو البروتوكولات منخفضة المستوى.

\n\n

2. الطريقة الاحترافية: Traits From و Into

\n

Rust لغة تجعل الأخطاء واضحة. لذلك توفر المكتبة القياسية traits From و Into لـ تحويل آمن لا يفشل أبداً.

\n

القاعدة بسيطة: إذا كان التحويل ناجحاً دائماً (بدون فقدان بيانات)، فإن الـ trait مُطبّق. مثال:

\n
let 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 غير مطبّق لأنه قد يحدث تجاوز.

\n

جرب ترجمة هذا الكود:

\n
let big: u16 = 256;\nlet small: u8 = big.into(); // خطأ في الترجمة!\n
\n

سيخبرك المترجم: "the trait `From` is not implemented for `u8`". هذه هي الأمان — لا يمكنك فقدان البيانات عن طريق الخطأ.

\n\n

3. عندما قد يفشل التحويل: TryFrom و TryInto

\n

ماذا لو أردنا تحويل u16 إلى u8 مع التحقق؟ هنا تأتي TryFrom و TryInto. ترجع هذه Result يجب معالجته.

\n
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.

المدونات

توصيات الكتب