Rust: ملكية Rust: المبادئ الأساسية الثلاثة لأمان الذاكرة

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

تعد «الملكية» السمة الأكثر تميزًا في لغة Rust — فهي تتيح لهذه اللغة ضمان أمان الذاكرة دون الحاجة إلى أداة جمع القمامة.

تعد «الملكية» الفرق الأساسي بين لغة «Rust» واللغات الأخرى. وبمجرد فهمها، فإنك تفهم جوهر تصميم لغة «Rust».


1. ما ستتعلمه



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

100%
flowchart LR
    A["Value Creation<br>let s = String::from(...)"] --> B["Owner Binding<br>s Has this value"]
    B --> C["Transfer of Ownership move<br>let s2 = s; s Failure"]
    C --> D["Out of scope drop<br>} Automatic Memory Release"]
    D --> E["Memory Security<br>No Double Release,Non-swinging pointer"]


3. قصة مكتبة

(1) المأزق: استعار شخصان الكتاب في نفس الوقت

يدير توم مكتبة صغيرة. وقد واجه مشكلة شائعة:

«لو كان من الممكن أن يستعير الكتاب شخص واحد فقط في كل مرة...»

(2) قواعد الملكية في Rust

RUST
fn main() {
    let book = String::from("Rust Programming");  // book is the owner of this book
    // let book2 = book;                   // ❌ If you write it this way,book Ownership was transferred to book2
    // println!("{}", book);               // ❌ book Can no longer be used

    // ✅ The Correct Approach:Only one person can hold ownership at a time.
    println!("{} Belongs to Tom The Library", book);
}  // book Automatically destroyed here——No manual intervention required free

قواعد الملكية في لغة Rust تشبه سياسة «شخص واحد في كل مرة» المتبعة في المكتبات: لكل قيمة مالك واحد فقط في أي وقت معين. وعندما يخرج المالك من نطاق الصلاحية، يتم إتلاف القيمة تلقائيًا.



4. المبادئ الثلاثة للملكية

100%
graph TB
    A[The Three Principles of Ownership] --> B[Principles1: Each value has exactly one owner.]
    A --> C[Principles2: It is destroyed as soon as it leaves its scope.]
    A --> D[Principles3: Ownership can be transferred(move)]
    B --> E[let s = String::from("hi")]
    C --> F[} Automatically call at the end drop]
    D --> G[let s2 = s;  // s No longer valid]

(1) شرح مفصل للمبادئ

المبدأ الوصف القياس
مالك واحد في أي وقت من الأوقات، لا «يمتلك» كل قيمة سوى شخص واحد لا يمكن استعارة كتاب من المكتبة إلا لشخص واحد في كل مرة
نهاية النطاق = التدمير يتم استدعاؤها تلقائيًا عندما يخرج المتغير من نطاقه drop يتم إرجاع الكتب تلقائيًا عند حلول موعد استحقاقها
نقل الملكية (النقل) يتم نقل الملكية أثناء التعيين أو تمرير المعلمات؛ وتصبح المتغير الأصلي غير صالح يتم تمرير كتاب من شخص إلى آخر


5. المكدسات والكومات

لفهم مفهوم الملكية، يجب عليك أولاً فهم الفرق بين المكدس (stack) والكومة (heap):

البعد المكدس الكومة
سرعة التخصيص سريعة جدًا (push/pop) أبطأ (تتطلب العثور على ذاكرة خالية)
تخزين البيانات بيانات ذات حجم معروف وقت التحويل البرمجي بيانات ذات حجم غير معروف أو متغير ديناميكيًا
الأنواع النموذجية i32، bool، f64، Array String، Vec، Box
إدارة الذاكرة تلقائية (إطارات مكدس استدعاءات الدوال) تتطلب إدارة يدوية أو إدارة قائمة على الملكية

(3) مقارنة بين «النسخ» و«الاستنساخ» و«النقل»

العملية الصيغة هل المتغير الأصلي صالح؟ العبء على الأداء الأنواع القابلة للتطبيق
نسخ let b = a; صالح منخفض جدًّا (نسخة بتية للمكدس) i32، f64، bool، char، المجموعات (بما في ذلك أنواع النسخ)
نسخة let b = a.clone(); صالحة عالية (تخصيص ذاكرة المكدس) String، Vec<T>، Box<T>
نقل let b = a; تنتهي صلاحيتها 0 (تنسخ المؤشر فقط) String، Vec<T>، Box<T>

إذا كان النوع يُنفِّذ Copy، فإن عملية التعيين تؤدي إلى نسخ تلقائي؛ أما إذا لم يُنفِّذ Copy، فإن عملية التعيين تؤدي إلى نقل تلقائي. استخدم .clone() لإجراء نسخ عميق صريح عندما تحتاج إلى الحفاظ على المتغير الأصلي.

RUST
fn main() {
    // Data on the Stack:Fixed size,Copy Semantics
    let x: i32 = 5;        // Allocation on the Stack 4 Byte
    let y = x;              // Make a copy, x and y are both valid.
    println!("x={}, y={}", x, y);  // ✅ All are acceptable

    // Load the data:Varies in size,move Semantics
    let s1 = String::from("hello");  // s1 On the stack(ptr/len/cap),The actual data is on the heap.
    let s2 = s1;                     // ❌ Ownership from s1 Transfer to s2
    // println!("{}", s1);            // ❌ Compilation Error:s1 Has been moved
    println!("{}", s2);              // ✅ s2 It's the new owner.
}


6. أمثلة على الملكية

(1) ▶ المثال:دلالة "move" — نقل الملكية (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// Demonstration of Ownership Transfer:Occurs during assignment move
// ============================================

fn main() {
    let s1 = String::from("Rust");
    let s2 = s1;  // s1 Ownership was transferred to s2

    // println!("s1: {}", s1);  // ❌ Compilation Error!s1 Has been moved
    println!("s2: {}", s2);     // ✅ s2 I am the owner now

    // Integer types are Copy, So it won't move
    let a = 42;
    let b = a;                 // a The value is copied to b
    println!("a: {}, b: {}", a, b);  // ✅ Both are effective.
}

الناتج:

TEXT 📖 للعرض فقط
s2: Rust
a: 42, b: 42

الأنواع القياسية مثل الأعداد الصحيحة تُنفِّذ السمة Copy، لذا فإن عمليات التعيين تنطوي على النسخ بدلاً من النقل. أما String فلا تُنفِّذ Copy، لذا فإن عمليات التعيين تنطوي على النقل. بعد عملية النقل، يصبح المتغير الأصلي غير صالح؛ وهذه ميزة تصميمية أساسية في لغة Rust تهدف إلى منع «التحرير المزدوج».


(2) ▶ المثال:تمرير الملكية في استدعاءات الدوال (مستوى الصعوبة ⭐⭐)

RUST
// ============================================
// Transfer of Ownership in Parameter Passing and Return Values
// ============================================

fn take_ownership(s: String) {
    println!("Acquire ownership: {}", s);
}  // s Destroyed here(drop)

fn give_ownership() -> String {
    let s = String::from("Newly created string");
    s  // Ownership is returned to the caller
}

fn main() {
    let s1 = String::from("hello");

    take_ownership(s1);  // s1 Ownership was transferred to the function
    // println!("{}", s1);  // ❌ s1 Expired

    let s2 = give_ownership();  // Acquiring Ownership from a Function
    println!("s2: {}", s2);     // ✅ s2 Ownership

    // Send it in and then send it back
    let s3 = String::from("Passing back and forth");
    let s3_back = takes_and_returns(s3);
    // println!("{}", s3);  // ❌ s3 Has been moved
    println!("s3_back: {}", s3_back);  // ✅
}

fn takes_and_returns(s: String) -> String {
    println!("Inside a function: {}", s);
    s  // Return ownership to the caller
}

الناتج:

TEXT 📖 للعرض فقط
Acquire ownership: hello
s2: Newly created string
Inside a function: Passing back and forth
s3_back: Passing back and forth

عند استدعاء دالة ما، تنتقل ملكية المعلمات التي تم تمريرها إلى الدالة. وعندما تُرجع الدالة قيمةً ما، تنتقل الملكية مرةً أخرى إلى المُستدعي. وهذا هو المقصود بعبارة «تدفق الملكية بين الدوال» — وهو نفس مبدأ «من يملك المفتاح يمكنه فتح الباب».


(3) ▶ المثال:مقارنة بين «النسخ» و«الاستنساخ» (مستوى الصعوبة: ⭐⭐⭐)

RUST
// ============================================
// Copy(Auto-Copy) vs Clone(Explicit Cloning)
// ============================================

fn main() {
    // --- Copy Type:Automatic Copy on Assignment ---
    let num1 = 100;
    let num2 = num1;         // Auto-Copy,num1 Still valid
    println!("Copy: num1={}, num2={}", num1, num2);  // ✅

    // --- Clone Type:Must be explicitly called .clone() ---
    let s1 = String::from("Cloning required");
    let s2 = s1.clone();     // Explicit Cloning,s1 Still valid
    println!("Clone: s1={}, s2={}", s1, s2);  // ✅

    // --- Neither Clone Nor Copy: only move ---
    let v1 = vec![1, 2, 3];
    let v2 = v1;  // move!v1 Failure
    // println!("{:?}", v1);  // ❌
    println!("v2: {:?}", v2);  // ✅
}

الناتج:

TEXT 📖 للعرض فقط
Copy: num1=100, num2=100
Clone: s1=Cloning required, s2=Cloning required
v2: [1, 2, 3]

النسخ: يقوم تلقائيًا بنسخ البيانات بتًا بتًا أثناء عملية التعيين؛ ويظل المتغير الأصلي ساريًا (ينطبق على البيانات الموجودة في المكدس). الاستنساخ: يتطلب استدعاءً صريحًا لطريقة .clone()؛ وينطبق على البيانات الموجودة في الكومة. عملية الاستنساخ أكثر تكلفة (تتطلب تخصيص ذاكرة في الكومة)، لذا صُممت لغة Rust بحيث «يجب عليك اختيار الاستنساخ صراحةً».


(4) ▶ المثال:أنماط الحصول على الملكية وإعادتها في الدوال (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// Common Patterns:Send it in and then send it back(Get+Return)
// ============================================

struct User {
    name: String,
    score: i32,
}

fn boost_score(mut user: User, bonus: i32) -> User {
    user.score += bonus;
    user
}

fn format_user(user: &User) -> String {
    format!("{}: {} pts", user.name, user.score)
}

fn main() {
    let alice = User {
        name: String::from("Alice"),
        score: 80,
    };

    println!("Before the upgrade: {}", format_user(&alice));

    let alice = boost_score(alice, 15);
    println!("After the upgrade: {}", format_user(&alice));

    let alice = boost_score(alice, 10);
    println!("Upgrade Again: {}", format_user(&alice));

    let mut bob = User {
        name: String::from("Bob"),
        score: 60,
    };

    let bonus = 20;
    bob.score += bonus;
    println!("Bob After the bonus points are added: {}", format_user(&bob));

    let names = vec![alice.name.clone(), bob.name.clone()];
    println!("All users: {:?}", names);

    println!("Alice Final Score: {}", alice.score);
    println!("Bob Final Score: {}", bob.score);
}

الناتج:

TEXT 📖 للعرض فقط
Before the upgrade: Alice: 80 pts
After the upgrade: Alice: 95 pts
Upgrade Again: Alice: 105 pts
Bob After the bonus points are added: Bob: 80 pts
All users: ["Alice", "Bob"]
Alice Final Score: 105
Bob Final Score: 80

يتم نقل ملكية الهياكل بنفس الطريقة المتبعة مع الأنواع الأساسية. boost_score يتم الحصول على الملكية من خلال معلمة ويتم إرجاعها بعد التعديل — وهذا هو نمط «الحصول والإرجاع». ويتيح استخدام المرجع (&User) الوصول للقراءة فقط دون الحصول على الملكية.


(5) ▶ المثال:التفاعل بين الملكية والمجموعات (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// Vec,String Common Interaction Patterns with Ownership
// ============================================

fn take_first_word(words: &Vec<String>) -> Option<&str> {
    words.first().map(|s| s.as_str())
}

fn remove_and_return(vec: &mut Vec<i32>, index: usize) -> Option<i32> {
    if index < vec.len() {
        Some(vec.remove(index))
    } else {
        None
    }
}

fn main() {
    let mut words = vec![
        String::from("hello"),
        String::from("rust"),
        String::from("world"),
    ];
    println!("Original vector: {:?}", words);

    if let Some(first) = take_first_word(&words) {
        println!("The First Word: {}", first);
    }

    let removed = remove_and_return(&mut words, 1);
    println!("Remove Index 1: {:?}, Remaining: {:?}", removed, words);

    let mut numbers = vec![10, 20, 30, 40, 50];
    while let Some(val) = numbers.pop() {
        println!("Pop up: {}", val);
    }
    println!("The vector is empty: {:?}", numbers);

    let s1 = String::from("foo");
    let s2 = String::from("bar");
    let combined = s1 + &s2;
    println!("Stitching Results: {}", combined);

    let data = vec![String::from("a"), String::from("b")];
    for word in &data {
        println!("Borrow: {}", word);
    }
    println!("The vector is still available: {:?}", data);

    let data2 = vec![1, 2, 3];
    for num in data2 {
        println!("Consumption: {}", num);
    }
}

الناتج:

TEXT 📖 للعرض فقط
Original vector: ["hello", "rust", "world"]
The First Word: hello
Remove Index 1: Some(20), Remaining: ["hello", "world"]
Pop up: 50
Pop up: 40
Pop up: 30
Pop up: 20
Pop up: 10
The vector is empty: []
Stitching Results: foobar
Borrow: a
Borrow: b
The vector is still available: ["a", "b"]
Consumption: 1
Consumption: 2
Consumption: 3

for item in &vec عملية تجول «استعارة» (يظل المتجه متاحًا)، for item in vec عملية تجول «استهلاك» (يتم نقل المتجه). + يستهلك العامل السلسلة الموجودة على اليسار. pop() يُرجع Option<T> ويستهلك العنصر الأخير.



❓ أسئلة شائعة

س لماذا تم تصميم نظام الملكية في لغة Rust؟ أليس من الأسهل استخدام آلية جمع القمامة؟
ج يتيح نظام الملكية للغة Rust ضمان أمان الذاكرة دون توقفات ناجمة عن آلية جمع القمامة.
س أين يذهب المتغير الأصلي بعد move؟
ج يقوم المُجمِّع بوضع علامة «غير صالح» على المتغير الأصلي.
س ما هي الأنواع القابلة للنسخ؟
ج جميع الأنواع القياسية (الأعداد الصحيحة، الأعداد العائمة، القيم المنطقية، الأحرف) والمجموعات/المصفوفات المكونة منها.
س أيهما يتمتع بأداء أفضل، .clone() أم move؟
ج لا تتطلب move أي عبء إضافي، بينما تتطلب clone تخصيص ذاكرة من الكومة.
س كيف تعمل الملكية مع البنيات؟
ج قواعد الملكية الخاصة بالبنية ككل هي نفسها التي تنطبق على المتغير الفردي.

📖 ملخص


📝 تمارين

  1. الصعوبة ⭐: اكتب برنامجًا يقوم بإنشاء متغير String، وتعيينه إلى متغير آخر، ثم يحاول طباعة المتغير الأول — لاحظ رسالة الخطأ التي يعرضها المُترجم.
  2. الصعوبة ⭐⭐: اكتب دالة fn append_world(s: String) -> String تضيف كلمة "world" في نهاية سلسلة أحرف وتُرجعها. استدعِها في main ولاحظ كيف يتم تمرير الملكية إلى الدالة ومنها.
  3. الصعوبة ⭐⭐⭐: عرّف دالة fn calculate_length(s: String) -> (String, usize) تُرجع سلسلة نصية وطولها، مع الاحتفاظ بملكية السلسلة النصية. تأكد من أنه لا يزال من الممكن استخدام المتغير الأصلي بعد استدعاء الدالة.
Web-Tutorial.com

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

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

100%