Rust: مدد صلاحية Rust: توضيح صلاحية المراجع

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

«Lifetime» هي الآلية التي تستخدمها لغة «Rust» لضمان «عدم تعطل المراجع أبدًا» — فهي تحدد «تاريخ انتهاء الصلاحية» لكل مرجع.

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


1. ما ستتعلمه



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

100%
flowchart LR
    subgraph "Function Signature fn longest<'a>"
        X["x: &'a str"] --> RET["Return Value: &'a str"]
        Y["y: &'a str"] --> RET
    end
    NOTE["'a = the shorter of x and y's lifetimes"] -.-> RET


3. قصة عن مطاردة فأر

(1) الإحباط: هربت الفأرة، لكنني ما زلت أراقبها

توم عالم أحياء يستخدم جهاز تتبع لتسجيل مواقع الفئران:

TEXT 📖 للعرض فقط
The tracker locks onto the mouse A → Mouse A ran into the cave → The tracker is still pointing toward the entrance.

«سيكون من الرائع لو كان جهاز التتبع يحمل ملصقًا مكتوبًا عليه: "صالح حتى يخرج الفأر «أ» من جحره»...»

(2) حلول لدورة حياة Rust

RUST
// Life Cycle Annotation 'a: Tell the compiler "the validity period of the return value does not exceed that of parameter x's validity period"
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let string1 = String::from("Long Strings");
    let result;

    {
        let string2 = String::from("short");
        result = longest(&string1, &string2);  // ✅ string2 and string1 are both still alive
        println!("The longer one is: {}", result);
    }  // string2 Destroyed here

    // println!("{}", result);  // ❌ If you print here, result references string2 which is destroyed
}  // string1 Destroyed here

فترات الصلاحية تشبه ملصقات «تاريخ انتهاء الصلاحية» الملصقة على أجهزة التتبع. يستخدم المُجمِّع هذه الملصقات لتحديد ما إذا كانت الإشارة لا تزال ضمن فترة صلاحيتها. في هذا المثال، لا يمكن أن تتجاوز فترة صلاحية result فترة صلاحية string2.



4. كيف تعمل دورة الحياة

(1) لكل مرجع دورة حياة

RUST
fn main() {
    let x: i32 = 10;           // x Its life cycle begins here
    let r: &i32 = &x;          // r Its life cycle begins here
    println!("r: {}", r);       // Usage r
}                               // r whose lifecycle ends first, then x ends

(2) لماذا من الضروري توضيح دورات الحياة؟

عندما تُرجع دالة ما مرجعًا، يتعين على المُترجم معرفة المعلمة التي يأتي منها هذا المرجع:

100%
graph TB
    A[Functions Return References] --> B{Which parameter does the return value point to?}
    B --> C[Pointer Parameters1 → The Lifecycle of Return Values ≤ Parameters1]
    B --> D[Pointer Parameters2 → The Lifecycle of Return Values ≤ Parameters2]
    B --> E[Point to one of the two → Must be labeled: Find the intersection]
    C --> F[No annotation required (the compiler can infer)]
    D --> F
    E --> G[Manual annotation is required 'a]
السيناريو المثال هل يتطلب تعليقًا توضيحيًّا؟
مرجع المدخلات الفردية fn first(x: &str) -> &str غير مطلوب (قاعدة الحذف)
مراجع إدخال متعددة fn longest(x: &str, y: &str) -> &str مطلوب (المترجم لا يعرف أيها يختار)
لا تُرجع مرجعًا fn len(x: &str) -> usize غير مطلوب
الهياكل التي تحتوي على مراجع struct S<'a> { r: &'a i32 } مطلوب

(3) مرجع سريع لمواقع التعليقات التوضيحية لدورة الحياة

الموقع الصيغة الوصف
تعريف الدالة fn foo<'a>(x: &'a str) -> &'a str المعلمات والقيم المرجعة
تعريف البنية struct S<'a> { r: &'a str } لا يمكن أن تستمر البنية في الوجود بعد انتهاء صلاحية مرجعها
كتلة impl impl<'a> S<'a> تحديد دورة الحياة عند تنفيذ دالة
قيود السمات where T: 'a لا يمكن أن تحتوي أنواع القيود على مراجع قصيرة الأمد
تعليق ثابت &'static str صالح طوال فترة تنفيذ البرنامج بأكمله


5. التعليقات التوضيحية لدورة الحياة

(1) قواعد النحو

RUST
// 'a is the name of the lifecycle parameter (using 'a, 'b, 'c)
// Pronunciation: Regarding the lifecycle 'a, both x and y need to be alive for at least 'a

fn function<'a>(x: &'a str, y: &'a str) -> &'a str {
    // The lifecycle of return values = the shorter of x and y
}

(2) قواعد حذف دورة الحياة

يمكن للمترجم أن يستنتج مدد الصلاحية تلقائيًا (دون الحاجة إلى كتابتها) في الحالات الثلاث التالية:

القاعدة المعنى
لكل مرجع إدخال دورة حياة خاصة به fn foo(x: &str) يعين تلقائيًا دورة حياة لـ x
مرجع إدخال واحد فقط مدة صلاحية مرجع الإخراج = مدة صلاحية مرجع الإدخال
مدخلات متعددة، أحدها هو &self أو &mut self مدة صلاحية مرجع المخرجات هي &self
RUST
// No annotation required: there is only one input reference
fn first_word(s: &str) -> &str { &s[..] }

// Needs annotation: two input references
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}


6. أمثلة على دورة الحياة

(1) ▶ المثال:لماذا نحتاج إلى دورة حياة؟ (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// What happens if there are no lifecycle annotations?
// ============================================

// This function has two input references, the return value could be any one of these
// ❌ If not annotated, a compilation error will occur: expected lifetime parameter
// fn longest_wrong(x: &str, y: &str) -> &str {
//     if x.len() > y.len() { x } else { y }
// }

// ✅ Correct Version: Lifecycle Annotation
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

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

    {
        let s2 = String::from("hi");
        result = longest(&s1, &s2);  // result lifecycle = s2 lifecycle (the shorter one)
        println!("The longer one is: {}", result);
    }  // s2 destroyed, result invalid

    // println!("{}", result);  // ❌ The compiler will prevent: result's lifecycle has ended
}

الناتج:

TEXT 📖 للعرض فقط
The longer one is: hello

يتم تعيين مدة الصلاحية 'a على أقصر المدة بين x وy. فإذا تم تعيين مدة صلاحية القيمة المرجعة على المدة الأطول، فقد تظل قيد الاستخدام بعد تدمير المدة الأقصر — وهذا بالضبط ما يحاول المُجمِّع منعه.


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

RUST
// ============================================
// The two parameters have different lifecycles.
// ============================================

// Two Lifecycles: 'a corresponds to x, 'b corresponds to y
// The return value depends only on x, so use 'a
fn choose_first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
    x  // Return only x, so the lifecycle only needs 'a
}

fn main() {
    let x = String::from("Live to be a hundred years old");
    let result;
    {
        let y = String::from("Short-lived ghost");
        result = choose_first(&x, &y);  // ✅ result Depends solely on x
        println!("result: {}", result);
    }  // y destroyed, but result does not depend on y, so no problem

    println!("Remains valid even after leaving the inner layer: {}", result);  // ✅ x Still Alive
}

الناتج:

TEXT 📖 للعرض فقط
result: Live to be a hundred years old
Remains valid even after leaving the inner layer: Live to be a hundred years old

عندما تعتمد قيمة الإرجاع حصريًّا على معلمة معينة، لا يتعين عليك سوى تحديد مدة حياة تلك المعلمة. فحتى لو كانت مدة حياة المعلمة choose_first الموجودة في y قصيرة، فإن ذلك لا يؤثر على النتيجة — لأن قيمة الإرجاع لا تستخدم أي بيانات من y على الإطلاق.


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

RUST
// ============================================
// Storing references in a structure -- the lifecycle must be specified.
// ============================================

struct Excerpt<'a> {
    content: &'a str,  // Structures Borrow External Strings
}

impl<'a> Excerpt<'a> {
    fn length(&self) -> usize {
        self.content.len()
    }

    fn announce(&self, announcement: &str) -> &str {
        println!("Announcement: {}", announcement);
        self.content  // Returning a Reference to the Interior of a Structure
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Can't find the period");

    let excerpt = Excerpt {
        content: &first_sentence,
    };

    println!("Excerpt: {}", excerpt.content);
    println!("Length: {}", excerpt.length());
    println!("Announcement of Results: {}", excerpt.announce("Welcome to this article"));
}  // excerpt must be destroyed before novel (content depends on novel)

الناتج:

TEXT 📖 للعرض فقط
Excerpt: Call me Ishmael
Length: 15
Announcement of Results: Call me Ishmael

عندما تحتوي بنية ما على مراجع، يجب تحديد معلمة مدة الصلاحية <'a> على اسم البنية. وهذا يُعلم المُترجم بأن مدة صلاحية مثيل البنية لا يمكن أن تتجاوز مدة صلاحية البيانات التي يشير إليها.


(4) ▶ المثال:دورة حياة «static» (مستوى الصعوبة ⭐⭐⭐)

RUST
// ============================================
// 'static: the reference remains valid throughout the program's execution.
// ============================================

fn main() {
    // A string literal is 'static
    let s: &'static str = "I exist throughout the entire lifecycle of a program";
    println!("{}", s);

    // 'static Constraints: T must not contain any non-static references
    fn print_static<T: 'static>(val: &T) {
        println!("{:?}", std::ptr::from_ref(val));
    }

    let num: i32 = 42;
    print_static(&num);  // ✅ i32 excludes references, complies with 'static

    // let msg = String::from("temp");
    // let r = &msg;
    // print_static(&r);  // ❌ r references non-'static msg
}

الناتج:

TEXT 📖 للعرض فقط
I exist throughout the entire lifecycle of a program
0x...(Memory Address)

'static هي أطول مدة بقاء في لغة Rust — حيث تظل البيانات موجودة من لحظة بدء البرنامج حتى نهايته. تتمتع القيم الثابتة من نوع السلسلة (String literals) بمدة بقاء 'static. يرجى ملاحظة ما يلي: ينبغي تفسير 'static على أنها تعني «هذه الإشارة صالحة إلى الأبد»، وليس «هذه البيانات تبقى حتى النهاية».


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

RUST
// ============================================
// Life Cycle in Practice: Zero-Copy Text Analysis
// ============================================

struct TextAnalyzer<'a> {
    text: &'a str,
}

impl<'a> TextAnalyzer<'a> {
    fn new(text: &'a str) -> Self {
        TextAnalyzer { text }
    }

    fn word_count(&self) -> usize {
        self.text.split_whitespace().count()
    }

    fn longest_word(&self) -> &'a str {
        self.text
            .split_whitespace()
            .max_by_key(|w| w.len())
            .unwrap_or("")
    }

    fn first_n_words(&self, n: usize) -> Vec<&'a str> {
        self.text.split_whitespace().take(n).collect()
    }

    fn line_count(&self) -> usize {
        self.text.lines().count()
    }

    fn char_count(&self) -> usize {
        self.text.chars().count()
    }

    fn summary(&self) -> String {
        format!(
            "Character: {}, Words: {}, Number of lines: {}, Longest Word: '{}'",
            self.char_count(),
            self.word_count(),
            self.line_count(),
            self.longest_word()
        )
    }
}

fn highlight_word<'a>(text: &'a str, word: &str) -> Vec<&'a str> {
    text.split_whitespace()
        .filter(|w| w.contains(word))
        .collect()
}

fn main() {
    let article = "Rust is a systems programming language \
that runs blazingly fast and prevents segfaults. \
Rust guarantees memory safety and thread safety.";

    let analyzer = TextAnalyzer::new(article);
    println!("=== Text Analysis ===");
    println!("{}", analyzer.summary());

    let top3 = analyzer.first_n_words(3);
    println!("\nFirst 3 words: {:?}", top3);

    let rust_mentions = highlight_word(article, "Rust");
    println!("Includes 'Rust' the word: {:?}", rust_mentions);

    let paragraph = "First line.\nSecond line.\nThird line.";
    let p_analyzer = TextAnalyzer::new(paragraph);
    println!("\nParagraph Analysis: {}", p_analyzer.summary());
}

الناتج:

TEXT 📖 للعرض فقط
=== Text Analysis ===
Character: 124, Words: 18, Number of lines: 1, Longest Word: 'blazingly'

First 3 words: ["Rust", "is", "a"]
Includes 'Rust' the word: ["Rust", "Rust"]

Paragraph Analysis: Character: 39, Words: 6, Number of lines: 3, Longest Word: 'Second'

TextAnalyzer يحتفظ بإشارة إلى النص الأصلي طوال دورة حياته <'a> — وجميع طرق التحليل لا تتطلب نسخًا. longest_word تشير القيمة المُرجعة &'a str إلى شرائح الكلمات في النص الأصلي ولا تؤدي إلى تخصيص أي ذاكرة.



❓ أسئلة شائعة

س هل «عمر الكائن» مفهوم يخص وقت التشغيل؟
ج لا! «عمر الكائن» هو مفهوم يخص وقت التحويل البرمجي بالكامل.
س ما معنى الحرف «a» في «a»؟
ج إنه مجرد اسم؛ ومن المعتاد استخدام a و b و c وهكذا دواليك.
س لماذا لا تظهر تعليقات مدة الصلاحية في معظم أكواد Rust؟
ج بسبب قاعدة حذف مدة الصلاحية، التي تسمح للمترجم باستنتاجها نيابة عنا.
س ما هي العلاقة بين مدة بقاء مرجع المدخلات «a» والقيمة المرجعة «a»؟
ج تتخذان التقاطع.
س متى أحتاج إلى تعريف دورة حياة بنفسي؟
ج هناك ثلاث حالات فقط: عندما تأخذ الدالة عدة مراجع إدخال وتُرجع مرجعًا، وعندما تحتوي البنية (struct) على مرجع، وعند تنفيذ سمة (trait) تفرض قيودًا على الأنواع المرتبطة.

📖 ملخص


📝 تمارين

  1. الصعوبة ⭐: اقرأ جزءًا من كود Rust موجود (مثل المثال الوارد في الدرس 9) وحدد الدوال التي تتجنب الحاجة إلى التوضيح اليدوي بفضل قاعدة حذف مدة الصلاحية.
  2. الصعوبة ⭐⭐⭐: اكتب دالة fn shortest<'a>(x: &'a str, y: &'a str) -> &'a str تُرجع أقصر سلسلتين. في main، أنشئ متغيرات ذات فترات صلاحية مختلفة لاستدعاء هذه الدالة.
  3. الصعوبة ⭐⭐⭐⭐: عرّف بنية Book<'a> تحتوي على حقل title من النوع &'a str. اكتب كتلة impl تُنفِّذ طريقة fn first_word(&self) -> &str تُرجع الكلمة الأولى من العنوان. اشرح كيفية استخدامها في main.
Web-Tutorial.com

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

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

100%