Rust: الهياكل في لغة Rust (Struct)

آخر تحديث: 2026-08-26

تُعد الهياكل (Struct) الأداة الأساسية لتعريف أنواع البيانات المخصصة في لغة Rust — فهي تجمع البيانات ذات الصلة في وحدة متكاملة ذات معنى.

إذا كان المتغير «بيانة واحدة»، فإن البنية هي «مجموعة من البيانات المترابطة». الأمر يشبه الجدول — فكل صف يمثل مثيلًا، وكل عمود يمثل حقلًا.


1. ما ستتعلمه



2. المخططات المفاهيمية

100%
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) التحدي: التعامل مع الطلاب ذوي المتغيرات المتباينة

ماريا معلمة تحتاج إلى إدارة المعلومات المتعلقة بطلاب فصلها. لكل طالب ما يلي:

في البداية، كتبت:

RUST
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

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. أنواع الهياكل

100%
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) ▶ المثال:الاستخدام الكامل للبنى المسماة (مستوى الصعوبة ⭐)

RUST
// ============================================
// 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);
}

الناتج:

TEXT 📖 للعرض فقط
Width: 30, Height: 50
Width after modification: 15
rect: Rectangle { width: 30, height: 50 }
rect (pretty): Rectangle {
    width: 30,
    height: 50,
}

#[derive(Debug)] تسمح بعرض الهياكل باستخدام تنسيق {:?} أو {:#?}. وتُعد هذه إحدى السمات المشتقة الأكثر استخدامًا أثناء عملية تصحيح الأخطاء.


(2) ▶ المثال:قواعد تحديث الهياكل واختصارات الحقول (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// 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
}

الناتج:

TEXT 📖 للعرض فقط
user2: User { username: "alice_new", email: "alice_new@example.com", active: true, sign_in_count: 1 }
user1.active: true

اختصار الحقل: عندما يتطابق اسم متغير مع اسم حقل في بنية، يمكن حذف النقطتين في field: field. تنسخ الصيغة المحدثة .. الحقول المتبقية من مثيل آخر — لاحظ أن هذه الصيغة تستخدم دلالات النقل بدلاً من النسخ.


(3) ▶ المثال:كتلة impl — إضافة طرق إلى بنية (مستوى الصعوبة: ⭐⭐)

RUST
// ============================================
// 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
}

الناتج:

TEXT 📖 للعرض فقط
rect1 Area: 1500
rect1 Can it accommodate? rect2: true
Area of a Square: 225

المعلمة الأولى لـ الطريقة (Method) هي &self (أو &mut self)، ويتم استدعاؤها عبر .. أما الدالة المرتبطة (Associated Function) فلا تحتوي على &self ويتم استدعاؤها عبر :: — ومن الأمثلة الكلاسيكية على ذلك String::from().


(4) ▶ المثال:هياكل التوبول (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// 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
}

الناتج:

TEXT 📖 للعرض فقط
Black: R=0, G=0, B=0
The Beginning: x=0, y=0, z=0

لا تحتوي بنية التوبول على أسماء حقول، بل تحتوي على أنواع فقط. وهي تشبه التوبول ولكن لها اسم نوع خاص بها. Color(0,0,0) وPoint(0,0,0) متطابقان داخليًّا ولكنهما من أنواع مختلفة — وهذا يمنع حدوث أي لبس.


(5) ▶ المثال:تمرين شامل — نظام إدارة الطلاب (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// 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());
    }
}

الناتج:

TEXT 📖 للعرض فقط
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
ج استخدم الطرق عندما تحتاج إلى معالجة بيانات المثيل، واستخدم الدوال المرتبطة عندما لا تحتاج إلى بيانات المثيل.
س ما الفرق بين &self و self؟
ج &self هي مرجع (لا ينطوي على نقل للملكية)، بينما self تستهلك المثيل (تنطوي على نقل للملكية).
س هل يمكن لـ struct تخزين مرجع؟
ج نعم، ولكن يجب تحديد مدة صلاحيته.
س ما وظيفة #[derive(Debug)]؟
ج تقوم تلقائيًا بتنفيذ السمة Debug للبنيات.

📖 ملخص


📝 تمارين

  1. الصعوبة ⭐: عرَّف بنية Book تحتوي على title: String وauthor: String وyear: u32. أنشئ مثيلين واطبع العنوان واسم المؤلف.
  2. الصعوبة ⭐⭐: أضف دالة fn perimeter(&self) -> u32 إلى Rectangle لحساب المحيط. أضف دالة مرتبطة بها fn from_width(w: u32) -> Rectangle لإنشاء مستطيل متساوي العرض والارتفاع.
  3. الصعوبة ⭐⭐⭐: عرّف بنية توبول Distance(f64, f64) لتمثيل المسافات (بالأمتار والسنتيمترات). قم بتنفيذ دالة fn to_meters(&self) -> f64 تُرجع إجمالي عدد الأمتار (على سبيل المثال، تُرجع Distance(1, 50) القيمة 1.50).
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%