Rust: متغيرات Rust وأنواع البيانات
آخر تحديث: 2026-08-26
في لغة Rust، تكون المتغيرات غير قابلة للتغيير افتراضيًّا — وهذا ليس قيدًا، بل ضمانًا للأمان.
يُعد نظام المتغيرات والأنواع في لغة «Rust» حجر الزاوية في التزامها بسلامة الذاكرة. ويُعد فهم مبدأ الثبات، واستنتاج الأنواع، والأنواع القياسية الخطوة الأولى نحو إتقان لغة «Rust».
1. ما ستتعلمه
- قم بتعريف متغير باستخدام
letلفهم مفهوم الثبات - إنشاء متغير قابل للتغيير باستخدام
let mut - استخدم
constلتعريف ثابت في وقت التحويل البرمجي - أنواع القيم العددية الأربعة في لغة Rust: الأعداد الصحيحة، والأعداد العائمة، والقيم المنطقية، والأحرف
- استدلال الأنواع والتعليق الصريح على الأنواع
- آلية التظليل المتغيرة
2. قصة حقيقية عن أمين صندوق
(1) التحدي: تصحيح الأخطاء في الأرقام في برنامج Excel
ليزا تعمل كصراف في متجر بقالة صغير. كل يوم بعد انتهاء العمل، تستخدم برنامج «إكسل» لحساب مبيعات اليوم:
- السعر يتغير باستمرار (بسبب العروض الترويجية والخصومات وأسعار الأعضاء)، لذا يتعين عليها تحديث الخانات يدويًّا.
- ذات مرة، قامت عن غير قصد باستبدال صف من الصيغ بأرقام، مما أدى إلى تلف جدول البيانات بأكمله.
- استغرق الأمر 3 ساعات لإجراء التسوية بين الحسابات مرة أخرى
بعد أن تعلمت لغة «Rust»، قالت: «لو كانت خلايا «Excel» غير قابلة للتعديل بشكل افتراضي، لما كان عليّ أن أقلق بشأن الكتابة فوق الصيغ».
(2) نموذج المتغيرات في لغة Rust
fn main() {
let total_sales: f64 = 1280.50; // Immutable by default -- Today's Sales
// total_sales = 1500.00; // ❌ Compilation Error: You cannot modify immutable variables!
let mut daily_changes = 0.0; // Use mut to declare a mutable variable
daily_changes = 150.0; // ✅ Can be modified
daily_changes = daily_changes + 200.0;
const TAX_RATE: f64 = 0.08; // Compile-time constant, Global Fixed
println!("Tax Rate: {}, Today's Accounts Receivable: {}", TAX_RATE, total_sales * (1.0 + TAX_RATE));
}
تتمثل فلسفة لغة «Rust» في: إذا كان من الممكن إبقاء الشيء على حاله، فلا تغيره. إن الثبات الافتراضي يجعل فهم الكود أسهل ويقلل من الأخطاء الناتجة عن التعديلات غير المقصودة.
3. إعلان المتغيرات
(1) let: غير قابل للتغيير افتراضيًا
let x = 5; // Immutable Variable
// x = 10; // ❌ Compilation Error: cannot assign twice to immutable variable
(2) let mut: المتغيرات القابلة للتغيير
let mut y = 5; // Mutable Variable
y = 10; // ✅ Can be reassigned
(3) const: ثابت وقت التحويل البرمجي
const MAX_POINTS: u32 = 100_000; // The type must be specified.
const PI: f64 = 3.1415926535;
| الخصائص | let |
let mut |
const |
|---|---|---|---|
| التغير | ثابت | قابل للتغيير | ثابت |
| موجود في وقت التشغيل | نعم | نعم | لا (مُدمج في وقت التحويل البرمجي) |
| تعليق النوع | اختياري | اختياري | إلزامي |
| النطاق العالمي | لا | لا | نعم |
| تقييم التعبير | وقت التشغيل | وقت التشغيل | ثابت وقت التحويل البرمجي |
4. أنواع القيم العددية
تحتوي لغة Rust على أربعة أنواع أساسية من القيم العددية: الأعداد الصحيحة، والأعداد العائمة، والقيم المنطقية، والأحرف.
graph TB
A[Scalar Types] --> B[Integer: i8/u8/i16/u16/i32/u32/i64/u64/i128/u128]
A --> C[Floating-point: f32/f64]
A --> D[Boolean: bool]
A --> E[Character: char]
(1) أنواع الأعداد الصحيحة
| الطول | موقّع | غير موقّع | النطاق |
|---|---|---|---|
| 8 بت | i8 |
u8 |
-128 ~ 127 / 0 ~ 255 |
| 16 بت | i16 |
u16 |
-32768 ~ 32767 / 0 ~ 65535 |
| 32 بت | i32 |
u32 |
±2.1 مليار / 0 ~ 4.2 مليار |
| 64 بت | i64 |
u64 |
±9.2 × 10¹⁸ / 0 ~ 1.8 × 10¹⁹ |
| 128 بت | i128 |
u128 |
المدى الأقصى |
| arch | isize |
usize |
يماثل عرض البت للنظام (نظام 64 بت = i64/u64) |
النوع الصحيح الافتراضي هو
i32— فهو يوفر أفضل أداء ويفي بمعظم الاحتياجات.
(2) أنواع الأرقام العائمة
let a: f32 = 3.14; // 32-bit precision floating-point (Single Precision)
let b: f64 = 3.141592653589793; // 64-bit precision floating-point (Double Precision, Default Type)
النوع الافتراضي للأرقام العائمة هو
f64— في المعالجات الحديثة، تتساوى سرعة f64 وf32 تقريبًا، لكن f64 يوفر دقة أعلى.
(3) النوع المنطقي
let is_ok: bool = true;
let is_not = false;
تُستخدم القيم المنطقية عادةً في الجمل الشرطية، مثل التعبيرات الشرطية if وwhile.
(4) أنواع الشخصيات
let c: char = 'A'; // ASCII Character
let emoji: char = '🦀'; // Unicode Character(4 Byte)
let han: char = 'Rust'; // Unicode character
في لغة Rust،
charهي قيمة عددية أحادية من Unicode تشغل 4 بايت. وهي ليست ASCII! وهذا يمنح لغة Rust دعمًا أصليًّا للتدويل.
| النوع | الحجم | نطاق القيم | الاستخدامات الشائعة |
|---|---|---|---|
bool |
1 بايت | true / false |
عمليات الفحص الشرطية، العلامات |
char |
4 بايت | قيمة عددية أحادية في يونيكود | معالجة حرف واحد |
f32 |
4 بايت | ±3.4×10³⁸ (بدقة تبلغ 7 أرقام تقريبًا) | الحوسبة الرسومية، وظائف التظليل في وحدة معالجة الرسومات (GPU) |
f64 |
8 بايت | ±1.8×10³⁰⁸ (دقة تبلغ 15 رقمًا تقريبًا) | الحوسبة العلمية (النقطة العائمة الافتراضية) |
i32 |
4 بايت | ±2.1 مليار | عدد صحيح للأغراض العامة (افتراضي) |
u8 |
1 بايت | 0–255 | بيانات البايت، قيم RGB |
usize |
arch | يساوي عرض البت للنظام | مؤشرات المصفوفات، حجم الحاوية |
5. استدلال الأنواع والتعليقات التوضيحية
(1) ▶ المثال:استنتاج النوع (مستوى الصعوبة ⭐)
// ============================================
// Rust compiler infers types based on assignments
// ============================================
fn main() {
let x = 42; // Inferred as i32 (Default integer type)
let y = 3.14; // Inferred as f64 (Default floating-point type)
let z = true; // Inferred as bool
let c = 'R'; // Inferred as char
// Can use :type to view (Example in This Section, Not a standard method)
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
الناتج:
x The value is: 42
y The value is: 3.14
يقوم المُجمِّع باستنتاج الأنواع تلقائيًا بناءً على السياق، لذا لا يحتاج المطورون إلى تحديد تعليقات الأنواع في كل مكان.
(2) ▶ المثال:التعليق التوضيحي الصريح للنوع (مستوى الصعوبة ⭐)
// ============================================
// Explicitly Specify the Type -- When a specific size or precision is required
// ============================================
fn main() {
let small: u8 = 255; // u8 Maximum value
let big: i64 = 9_223_372_036_854_775_807; // An underscore can be used as a digit separator
let precise: f32 = 3.141592653589793; // f32 May result in loss of precision
let flag: bool = 5 > 3; // The result of a Boolean expression
println!("small: {}, big: {}, precise: {:.10}, flag: {}", small, big, precise, flag);
}
الناتج:
small: 255, big: 9223372036854775807, precise: 3.1415927410, flag: true
لاحظ أن
f32لا تتجاوز دقتها حوالي 7 أرقام معنوية، وأن3.141592653589793تم اقتطاعها لتصبح3.1415927410.
(3) ▶ المثال:حجب المتغيرات (مستوى الصعوبة ⭐⭐)
// ============================================
// Shadowing: Overwrite a previous variable with the same name
// ============================================
fn main() {
let x = 5; // First x
let x = x + 1; // Shadowing: Second x, value becomes 6
let x = x * 2; // Shadowing: Third x, value becomes 12
// Shadowing allows for a change in type!
let name = "Rust"; // &str Type
let name = name.len(); // became usize Type!
println!("x value: {}", x);
println!("name length: {}", name);
}
الناتج:
x value: 12
name length: 4
التظليل ≠ القابلية للتغيير. يؤدي التظليل إلى إنشاء متغير جديد (ذاكرة جديدة)، بينما
mutيُعدّل نفس كتلة الذاكرة. يمكن للتظليل تغيير نوع المتغير ضمن نفس النطاق، لكنmutلا يستطيع ذلك.
(4) ▶ المثال:تمرين شامل — أداة إدارة المخزون (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// Comprehensive Example: Using Variables, Shadowing and Constants in Inventory Management
// Demo let/mut/const/shadowing Practical Applications
// ============================================
const MAX_STOCK: u32 = 10_000;
const DISCOUNT_THRESHOLD: u32 = 500;
fn main() {
let item_name = "Wireless Mouse";
let price: f64 = 49.99;
let mut stock: u32 = 2500;
println!("=== Inventory Management System ===");
println!("Products: {}", item_name);
println!("Unit Price: {:.2} yuan", price);
println!("Inventory: {} items", stock);
stock = stock - 300;
println!("\n--- Shipped Today 300 items ---");
println!("Remaining Inventory: {} items", stock);
let price = price * 0.85;
println!("\n--- Member Discount 15% off ---");
println!("Member Price: {:.2} yuan", price);
let price = format!("{:.2} yuan", price);
println!("Price Tag: {}", price);
let total_value = (stock as f64) * 49.99;
println!("\nTotal Inventory Value: {:.2} yuan", total_value);
if stock > DISCOUNT_THRESHOLD {
println!("Ample inventory (> {}), No need to restock", DISCOUNT_THRESHOLD);
} else {
let needed = MAX_STOCK - stock;
println!("Out of Stock! Suggested Reorder {} items", needed);
}
println!("Maximum Capacity: {} items", MAX_STOCK);
}
الناتج:
=== Inventory Management System ===
Products: Wireless Mouse
Unit Price: 49.99 yuan
Inventory: 2500 items
--- Shipped Today 300 items ---
Remaining Inventory: 2200 items
--- Member Discount 15% off ---
Member Price: 42.49 yuan
Price Tag: 42.49 yuan
Total Inventory Value: 109978.00 yuan
Ample inventory (> 500), No need to restock
Maximum Capacity: 10000 items
يجمع هذا المثال بين استخدام الثابت
const، والمتغير غير القابل للتغييرlet، والمتغير القابل للتغييرlet mut، وتظليل المتغيرات (حيث يتغير النوع المُظلَّل منf64إلىString)، مما يوضح كيفية تضافر طرق إعلان المتغيرات المختلفة هذه معًا في سيناريو واقعي.
(5) ▶ المثال:تجاوز سعة الأعداد الصحيحة والحساب الآمن (مستوى الصعوبة ⭐⭐)
// ============================================
// Integer Overflow Behavior: debug mode panic vs release mode wrapping
// ============================================
fn main() {
let max_u8: u8 = 255;
println!("u8 Maximum value: {}", max_u8);
// Safe Computation Method (No panic, Returns overflow result)
let (result, did_overflow) = max_u8.overflowing_add(1);
println!("255 + 1 (overflowing_add): {}, Did overflow?: {}", result, did_overflow);
let checked = max_u8.checked_add(1);
println!("255 + 1 (checked_add): {:?}", checked);
let saturating = max_u8.saturating_add(100);
println!("255 + 100 (saturating_add): {}", saturating);
let wrapping: u8 = max_u8.wrapping_add(1);
println!("255 + 1 (wrapping_add): {}", wrapping);
let neg_i8: i8 = -128;
let (abs_result, overflow) = neg_i8.overflowing_abs();
println!("i8 absolute value of minimum: {}, Did overflow?: {}", abs_result, overflow);
}
الناتج:
u8 Maximum value: 255
255 + 1 (overflowing_add): 0, Did overflow?: true
255 + 1 (checked_add): None
255 + 100 (saturating_add): 255
255 + 1 (wrapping_add): 0
i8 absolute value of minimum: -128, Did overflow?: true
في لغة Rust، يؤدي تجاوز سعة الأعداد الصحيحة إلى حدوث حالة «panic» في وضع التصحيح، ولكنه يؤدي إلى «التفاف» في وضع الإصدار. يُنصح باستخدام الطرق
checked_*وsaturating_*وwrapping_*للتحكم بشكل صريح في سلوك التجاوز.
(6) ▶ المثال:التطبيق العملي لتظليل المتغيرات وتحويل الأنواع (مستوى الصعوبة: ⭐⭐⭐)
// ============================================
// Practical Examples of Variable Shadowing: String Parsing Chain
// ============================================
fn main() {
let input = "42.5";
println!("Original Input: {} (Type: &str)", input);
let input = input.parse::<f64>();
println!("Analysis Results: {:?}", input);
let input = match input {
Ok(value) => value,
Err(_) => 0.0,
};
println!("After unpacking: {} (Type: f64)", input);
let input = input as i32;
println!("Round to an integer: {} (Type: i32, Truncation of Decimals)", input);
let input = input * 2;
println!("After doubling: {} (Type: i32)", input);
let input = format!("Result: {}", input);
println!("Final Output: {} (Type: String)", input);
let alice_score = "95";
let alice_score = alice_score.parse::<u32>().unwrap_or(0);
let alice_grade = if alice_score >= 90 { "A" } else { "B" };
println!("Alice: Score={}, Level={}", alice_score, alice_grade);
}
الناتج:
Original Input: 42.5 (Type: &str)
Analysis Results: Ok(42.5)
After unpacking: 42.5 (Type: f64)
Round to an integer: 42 (Type: i32, Truncation of Decimals)
After doubling: 84 (Type: i32)
Final Output: Result: 84 (Type: String)
Alice: Score=95, Level=A
يتيح «تظليل المتغيرات» التحويل التدريجي لأنواع البيانات ضمن نفس النطاق — من
&str→Result<f64>→f64→i32→String— باستخدام نفس اسم المتغير في كل خطوة، مما ينتج عنه كود واضح وخالٍ من التكرار.
❓ أسئلة شائعة
i32 بدلاً من i64؟let mut؟mut يُعدِّل المتغير نفسه (دون تغيير نوعه).char يشغل 4 بايت، ألا تُعد السلاسل مضيعة للمساحة؟char.const وlet؟const في النص في وقت التحويل البرمجي، بينما يتم تخصيص let في وقت التشغيل.📖 ملخص
- متغيرات Rust غير قابلة للتغيير بشكل افتراضي؛ استخدم
mutلإعلان متغيرات قابلة للتغيير constيجب أن تُرفق الثوابت في وقت الترجمة بتعليقات توضيحية للنوع وأن تتبع قاعدة التسمية التي تقضي باستخدام الأحرف الكبيرة فقط- تُصنف أنواع الأعداد الصحيحة إلى أنواع ذات إشارة (i) وأنواع بدون إشارة (u)؛ بشكل افتراضي
i32 - التنسيق الافتراضي للأرقام العائمة هو
f64؛ أماcharفيشغل 4 بايت ويدعم نظام يونيكود. - يتمتع مُجمِّع لغة Rust بقدرات استدلال الأنواع القوية، ولكن يمكنك تحديد الأنواع صراحةً عند الحاجة.
- التظليل المتغير يسمح لمتغير جديد يحمل نفس الاسم بأن يحل محل متغير قديم؛ بل ويمكنه حتى تغيير نوعه.
📝 تمارين
- الصعوبة ⭐: قم بتعريف متغير من النوع
u16وقم بتعيين القيمة 65535 له، ثم حاول تغييرها إلى 65536 ولاحظ رسالة الخطأ التي يعرضها المُترجم. - الصعوبة ⭐⭐: اكتب برنامجًا يستخدم
let mutلإعلان متغير يحسب مجموع الأعداد من 1 إلى 5، ثم يعرض النتيجة. - الصعوبة ⭐⭐⭐: حاول تعريف الثابت
constخارج الدالةfn main()واستخدامه داخل الدالة. ثم حاول تعريف المتغيرletخارج الدالة ولاحظ خطأ المُترجم — سيساعدك ذلك على فهم النطاق العام لـconst.