Rust: الهياكل في لغة Rust (Struct)
آخر تحديث: 2026-08-26
تُعد الهياكل (Struct) الأداة الأساسية لتعريف أنواع البيانات المخصصة في لغة Rust — فهي تجمع البيانات ذات الصلة في وحدة متكاملة ذات معنى.
إذا كان المتغير «بيانة واحدة»، فإن البنية هي «مجموعة من البيانات المترابطة». الأمر يشبه الجدول — فكل صف يمثل مثيلًا، وكل عمود يمثل حقلًا.
1. ما ستتعلمه
- استخدم الكلمة الرئيسية
structلتعريف بنية مسماة - إنشاء مثيل لهيكل (struct) والوصول إلى حقوله
- إضافة طرق ووظائف مرتبطة بها باستخدام كتلة
impl - بنية التوبول وبنية الوحدة
- ملكية حقول البنية ومراجعها
- قواعد تحديث الهياكل
..و#[derive(Debug)]
2. المخططات المفاهيمية
flowchart LR
subgraph "struct Definition"
DEF["struct User"]
F1["username: String"]
F2["email: String"]
F3["active: bool"]
F4["sign_in_count: u64"]
end
subgraph "Instantiation"
INS["let user = User { ... }"]
V1["username: 'alice'"]
V2["email: 'alice@example.com'"]
V3["active: true"]
V4["sign_in_count: 1"]
end
F1 -.->|"Field Mapping"| V1
F2 -.->|"Field Mapping"| V2
F3 -.->|"Field Mapping"| V3
F4 -.->|"Field Mapping"| V4
3. قصة نظام إدارة الطلاب
(1) التحدي: التعامل مع الطلاب ذوي المتغيرات المتباينة
ماريا معلمة تحتاج إلى إدارة المعلومات المتعلقة بطلاب فصلها. لكل طالب ما يلي:
- الاسم، العمر، الصف الدراسي، المعدل التراكمي
في البداية، كتبت:
let name1 = "Xiao Ming";
let age1 = 18;
let grade1 = "Senior Year";
let gpa1 = 3.8;
let name2 = "Xiao Hong";
let age2 = 17;
let grade2 = "11th Grade";
let gpa2 = 3.5;
عندما يرتفع عدد الطلاب إلى 40، تصبح هذه المتغيرات المتناثرة غير قابلة للإدارة على الإطلاق. فطباعة كشف الدرجات تتطلب كتابة 40 سطراً من التعليمات البرمجية، وتغيير حقل واحد يعني إجراء تعديلات في أربعة أماكن مختلفة.
(2) نهج «الستروكت» في لغة Rust
struct Student {
name: String,
age: u8,
grade: String,
gpa: f64,
}
fn main() {
let student1 = Student {
name: String::from("Xiao Ming"),
age: 18,
grade: String::from("Senior 3"),
gpa: 3.8,
};
let student2 = Student {
name: String::from("Xiao Hong"),
age: 17,
grade: String::from("Senior 2"),
gpa: 3.5,
};
println!("{}: {} years old, Grade: {}, GPA: {:.1}",
student1.name, student1.age, student1.grade, student1.gpa);
println!("{}: {} years old, Grade: {}, GPA: {:.1}",
student2.name, student2.age, student2.grade, student2.gpa);
}
تُشبه البنية (struct) «قالبًا» — فهي تحدد المعلومات التي يجب أن يحتوي عليها «الطالب». وكل مثيل يشبه نموذجًا مملوءًا. ويتم الوصول إلى الحقول عبر
.، وهي طريقة واضحة وآمنة.
4. أنواع الهياكل
graph TB
A[struct Type] --> B[Naming Structures: Fields have names and types]
A --> C[Tuple Structure: The field has a type but no name]
A --> D[Unit Structure: No fields]
B --> E[struct User { name: String, age: u8 }]
C --> F[struct Color(i32, i32, i32)]
D --> G[struct EmptyMarker]
| النوع | التعريف | حالات الاستخدام |
|---|---|---|
| قواعد تسمية الحقول | struct S { f1: T, f2: T } |
في معظم الحالات، تكون أسماء الحقول واضحة بذاتها |
| بنية التوبول | struct S(T1, T2) |
تُغلف مفهومًا واحدًا (مثل: لون RGB، الإحداثيات) |
| هيكل الوحدة | struct S; |
علامة النوع، عنصر نائب لتنفيذ السمة |
(2) الطرق مقابل دوال الارتباط
| النوع | المعلمة الأولى | اتفاقية الاستدعاء | الملكية | الاستخدامات الشائعة |
|---|---|---|---|---|
| الطريقة | &self |
obj.method() |
الاستعارة | قراءة بيانات المثيل |
| الطريقة | &mut self |
obj.method() |
استعارة المتغيرات | تعديل بيانات المثيل |
| الطريقة | self |
obj.method() |
الاستهلاك | تحويل/إتلاف المثيل |
| الوظائف ذات الصلة | لا شيء | Type::function() |
— | منشئات، طرق المصنع |
(3) مرجع سريع للصفات المشتقة الشائعة الاستخدام
| السمة | الوظيفة | الشروط التلقائية | مثال |
|---|---|---|---|
Debug |
{:?} طباعة منسقة |
تصحيح جميع الحقول | #[derive(Debug)] |
Clone |
.clone() نسخ عميق |
استنساخ جميع الحقول | #[derive(Clone)] |
Copy |
النسخ التلقائي عند التعيين | استنساخ + نسخ جميع الحقول | #[derive(Copy, Clone)] |
PartialEq |
== / != مقارنة |
جميع الحقول تطبق PartialEq | #[derive(PartialEq)] |
Hash |
تُستخدم كمفتاح في HashMap | جميع الحقول تُنفِّذ واجهة Hash | #[derive(Hash)] |
Default |
القيمة الافتراضية | جميع الحقول لها قيمة افتراضية | #[derive(Default)] |
5. أمثلة على البنية
(1) ▶ المثال:الاستخدام الكامل للبنى المسماة (مستوى الصعوبة ⭐)
// ============================================
// Naming Structures: Definition, Instantiation, Field Access
// ============================================
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
// Create an Instance
let rect = Rectangle {
width: 30,
height: 50,
};
// Access Fields
println!("Width: {}, Height: {}", rect.width, rect.height);
// Variable Instances
let mut mutable_rect = Rectangle {
width: 10,
height: 20,
};
mutable_rect.width = 15; // ✅ Can be modified
println!("Width after modification: {}", mutable_rect.width);
// Use Debug Print
println!("rect: {:?}", rect);
println!("rect (pretty): {:#?}", rect);
}
الناتج:
Width: 30, Height: 50
Width after modification: 15
rect: Rectangle { width: 30, height: 50 }
rect (pretty): Rectangle {
width: 30,
height: 50,
}
#[derive(Debug)]تسمح بعرض الهياكل باستخدام تنسيق{:?}أو{:#?}. وتُعد هذه إحدى السمات المشتقة الأكثر استخدامًا أثناء عملية تصحيح الأخطاء.
(2) ▶ المثال:قواعد تحديث الهياكل واختصارات الحقول (مستوى الصعوبة ⭐⭐)
// ============================================
// Initializing Field Abbreviations + Update Syntax
// ============================================
#[derive(Debug)]
struct User {
username: String,
email: String,
active: bool,
sign_in_count: u64,
}
fn build_user(username: String, email: String) -> User {
User {
username, // When field names and variable names are the same, they can be written in shorthand.
email, // Equivalent to email: email
active: true,
sign_in_count: 1,
}
}
fn main() {
let user1 = build_user(
String::from("alice"),
String::from("alice@example.com"),
);
// Update Syntax: Based on user1 create user2, modify only email and username
let user2 = User {
email: String::from("alice_new@example.com"),
username: String::from("alice_new"),
..user1 // Other fields copied from user1 (Note: String fields will move!)
};
// println!("user1: {}", user1.username); // ❌ username was moved to user2
println!("user2: {:?}", user2);
println!("user1.active: {}", user1.active); // ✅ bool is Copy, still valid
}
الناتج:
user2: User { username: "alice_new", email: "alice_new@example.com", active: true, sign_in_count: 1 }
user1.active: true
اختصار الحقل: عندما يتطابق اسم متغير مع اسم حقل في بنية، يمكن حذف النقطتين في
field: field. تنسخ الصيغة المحدثة..الحقول المتبقية من مثيل آخر — لاحظ أن هذه الصيغة تستخدم دلالات النقل بدلاً من النسخ.
(3) ▶ المثال:كتلة impl — إضافة طرق إلى بنية (مستوى الصعوبة: ⭐⭐)
// ============================================
// impl block: Methods (&self) and associated functions (no &self)
// ============================================
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// Methods: &self is a reference to a Rectangle instance
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
// Associative Functions (similar to static methods): no &self
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 20,
};
let square = Rectangle::square(15); // Correlation functions are used to :: Call
println!("rect1 Area: {}", rect1.area()); // 1500
println!("rect1 Can it accommodate? rect2: {}", rect1.can_hold(&rect2)); // true
println!("Area of a Square: {}", square.area()); // 225
}
الناتج:
rect1 Area: 1500
rect1 Can it accommodate? rect2: true
Area of a Square: 225
المعلمة الأولى لـ الطريقة (Method) هي
&self(أو&mut self)، ويتم استدعاؤها عبر.. أما الدالة المرتبطة (Associated Function) فلا تحتوي على&selfويتم استدعاؤها عبر::— ومن الأمثلة الكلاسيكية على ذلكString::from().
(4) ▶ المثال:هياكل التوبول (مستوى الصعوبة ⭐⭐)
// ============================================
// Tuple Structure: Suitable for "it's just a bunch of values" scenarios
// ============================================
#[derive(Debug)]
struct Color(i32, i32, i32); // RGB Color
#[derive(Debug)]
struct Point(i32, i32, i32); // 3D Coordinates
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
// Fields Accessed via Indexes (tuple-like)
println!("Black: R={}, G={}, B={}", black.0, black.1, black.2);
println!("The Beginning: x={}, y={}, z={}", origin.0, origin.1, origin.2);
// Tuple structures are not the same as tuples!
// let wrong: Color = origin; // ❌ Compilation Error: Point cannot be assigned to Color
// Although their internal structures are the same, they are different types
// This prevents "mixing up colors and coordinates" bugs
}
الناتج:
Black: R=0, G=0, B=0
The Beginning: x=0, y=0, z=0
لا تحتوي بنية التوبول على أسماء حقول، بل تحتوي على أنواع فقط. وهي تشبه التوبول ولكن لها اسم نوع خاص بها.
Color(0,0,0)وPoint(0,0,0)متطابقان داخليًّا ولكنهما من أنواع مختلفة — وهذا يمنع حدوث أي لبس.
(5) ▶ المثال:تمرين شامل — نظام إدارة الطلاب (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// Comprehensive Example: Structure-Based Approach, Associated Functions and Update Syntax
// ============================================
#[derive(Debug, Clone)]
struct Student {
name: String,
age: u8,
scores: Vec<u32>,
}
impl Student {
fn new(name: &str, age: u8) -> Self {
Student {
name: name.to_string(),
age,
scores: Vec::new(),
}
}
fn add_score(&mut self, score: u32) {
self.scores.push(score);
}
fn average(&self) -> f64 {
if self.scores.is_empty() {
return 0.0;
}
let sum: u32 = self.scores.iter().sum();
sum as f64 / self.scores.len() as f64
}
fn grade(&self) -> char {
match self.average() as u32 {
90..=100 => 'A',
80..=89 => 'B',
70..=79 => 'C',
60..=69 => 'D',
_ => 'F',
}
}
fn summary(&self) -> String {
format!("{} ({} years old) Average: {:.1} Level: {}", self.name, self.age, self.average(), self.grade())
}
}
fn main() {
let mut alice = Student::new("Alice", 20);
alice.add_score(95);
alice.add_score(88);
alice.add_score(92);
println!("{}", alice.summary());
let mut bob = Student::new("Bob", 21);
bob.add_score(72);
bob.add_score(65);
bob.add_score(58);
println!("{}", bob.summary());
let mut charlie = Student {
name: String::from("Charlie"),
..alice.clone()
};
charlie.name = String::from("Charlie");
charlie.add_score(100);
println!("{}", charlie.summary());
let students = [&alice, &bob, &charlie];
println!("\n=== Class Rankings ===");
let mut ranked: Vec<_> = students.iter().collect();
ranked.sort_by(|a, b| b.average().partial_cmp(&a.average()).unwrap());
for (i, s) in ranked.iter().enumerate() {
println!("#{}: {}", i + 1, s.summary());
}
}
الناتج:
Alice (20 years old) Average: 91.7 Level: A
Bob (21 years old) Average: 65.0 Level: D
Charlie (20 years old) Average: 93.8 Level: A
=== Class Rankings ===
#1: Charlie (20 years old) Average: 93.8 Level: A
#2: Alice (20 years old) Average: 91.7 Level: A
#3: Bob (21 years old) Average: 65.0 Level: D
يجمع هذا المثال بين استخدام دالة الارتباط
new()، والطرقadd_score()/average()/grade()، وبناءات التحديث..alice.clone()و#[derive(Debug, Clone)]. وتُعد الهياكل الأدوات الأساسية لتنظيم البيانات والطرق ذات الصلة.
❓ أسئلة شائعة
&self) والدوال المرتبطة (بدون &self)؟📖 ملخص
- الهياكل المسماة هي النوع الأكثر استخدامًا من الهياكل، وتتميز بأسماء حقول واضحة
- هياكل التوبول مناسبة للحالات التي تتضمن «مجرد مجموعة من القيم» (RGB، الإحداثيات)
- هياكل الوحدات لا تحتوي على حقول وتُستخدم لتعليق الأنواع.
implإضافة الطرق (&self) والدوال المرتبطة (لا توجد&self) إلى البنية- تهيئة اختصارات الحقول: يُحذف الفاصلة المائلة عندما يتطابق اسم المتغير مع اسم الحقل
- تحديث قواعد الصياغة
..other: نسخ الحقول المتبقية من مثيل #[derive(Debug)]جعل الهياكل قابلة للطباعة
📝 تمارين
- الصعوبة ⭐: عرَّف بنية
Bookتحتوي علىtitle: Stringوauthor: Stringوyear: u32. أنشئ مثيلين واطبع العنوان واسم المؤلف. - الصعوبة ⭐⭐: أضف دالة
fn perimeter(&self) -> u32إلىRectangleلحساب المحيط. أضف دالة مرتبطة بهاfn from_width(w: u32) -> Rectangleلإنشاء مستطيل متساوي العرض والارتفاع. - الصعوبة ⭐⭐⭐: عرّف بنية توبول
Distance(f64, f64)لتمثيل المسافات (بالأمتار والسنتيمترات). قم بتنفيذ دالةfn to_meters(&self) -> f64تُرجع إجمالي عدد الأمتار (على سبيل المثال، تُرجعDistance(1, 50)القيمة1.50).