Rust: مدد صلاحية Rust: توضيح صلاحية المراجع
آخر تحديث: 2026-08-26
«Lifetime» هي الآلية التي تستخدمها لغة «Rust» لضمان «عدم تعطل المراجع أبدًا» — فهي تحدد «تاريخ انتهاء الصلاحية» لكل مرجع.
في الواقع، أنت تستخدم بالفعل فترات الصلاحية — فجميع الأكواد الواردة في الدرسين 9 و10 لها فترات صلاحية؛ لكن المُجمِّع هو الذي «خمن» هذه الفترات نيابة عنك. يشرح لك هذا الدرس المواقف التي تحتاج فيها إلى تحديد فترات الصلاحية يدويًّا.
1. ما ستتعلمه
- لماذا نحتاج إلى دورة حياة؟ — لمنع حدوث المراجع المعلقة
- صيغة التعليقات التوضيحية لدورة الحياة:
'a،'b - فترات الصلاحية في توقيعات الدوال — فترات صلاحية المعلمات وقيم الإرجاع
- قواعد الإغفال مدى الحياة — متى يمكن للمُجمع الاستدلال تلقائيًا؟
- التعليقات التوضيحية لدورة الحياة في الهياكل
'staticدورة الحياة — المراجع الدائمة
2. المخططات المفاهيمية
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) الإحباط: هربت الفأرة، لكنني ما زلت أراقبها
توم عالم أحياء يستخدم جهاز تتبع لتسجيل مواقع الفئران:
The tracker locks onto the mouse A → Mouse A ran into the cave → The tracker is still pointing toward the entrance.
- يشير المتتبع (المرجع) إلى الماوس أ (البيانات)
- هرب الماوس «أ» (تم إتلاف البيانات)
- لا يزال المُتتبع يشير إلى ذلك الموقع (مرجع معلق — أمر خطير!)
«سيكون من الرائع لو كان جهاز التتبع يحمل ملصقًا مكتوبًا عليه: "صالح حتى يخرج الفأر «أ» من جحره»...»
(2) حلول لدورة حياة 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) لكل مرجع دورة حياة
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) لماذا من الضروري توضيح دورات الحياة؟
عندما تُرجع دالة ما مرجعًا، يتعين على المُترجم معرفة المعلمة التي يأتي منها هذا المرجع:
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) قواعد النحو
// '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 |
// 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) ▶ المثال:لماذا نحتاج إلى دورة حياة؟ (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// 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
}
الناتج:
The longer one is: hello
يتم تعيين مدة الصلاحية
'aعلى أقصر المدة بينxوy. فإذا تم تعيين مدة صلاحية القيمة المرجعة على المدة الأطول، فقد تظل قيد الاستخدام بعد تدمير المدة الأقصر — وهذا بالضبط ما يحاول المُجمِّع منعه.
(2) ▶ المثال:المعلمات لدورات حياة مختلفة (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// 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
}
الناتج:
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) ▶ المثال:دورة الحياة في الهياكل (مستوى الصعوبة ⭐⭐⭐⭐)
// ============================================
// 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)
الناتج:
Excerpt: Call me Ishmael
Length: 15
Announcement of Results: Call me Ishmael
عندما تحتوي بنية ما على مراجع، يجب تحديد معلمة مدة الصلاحية
<'a>على اسم البنية. وهذا يُعلم المُترجم بأن مدة صلاحية مثيل البنية لا يمكن أن تتجاوز مدة صلاحية البيانات التي يشير إليها.
(4) ▶ المثال:دورة حياة «static» (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// '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
}
الناتج:
I exist throughout the entire lifecycle of a program
0x...(Memory Address)
'staticهي أطول مدة بقاء في لغة Rust — حيث تظل البيانات موجودة من لحظة بدء البرنامج حتى نهايته. تتمتع القيم الثابتة من نوع السلسلة (String literals) بمدة بقاء'static. يرجى ملاحظة ما يلي: ينبغي تفسير'staticعلى أنها تعني «هذه الإشارة صالحة إلى الأبد»، وليس «هذه البيانات تبقى حتى النهاية».
(5) ▶ المثال:تمرين شامل — محلل النصوص (مستوى الصعوبة ⭐⭐⭐)
// ============================================
// 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 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إلى شرائح الكلمات في النص الأصلي ولا تؤدي إلى تخصيص أي ذاكرة.
❓ أسئلة شائعة
📖 ملخص
- أعمار هي آلية تُستخدم في وقت التحويل البرمجي في لغة Rust لضمان بقاء المراجع صالحة.
- استخدم صيغة
'aلربط فترات صلاحية المراجع المدخلة والمخرجة - قواعد استبعاد دورة الحياة تلغي الحاجة إلى إضافة التعليقات اليدوية في أكثر من 80% من الحالات
- عند تخزين المراجع في بنية (struct)، يجب تحديد معلمة مدة الصلاحية.
'staticله أطول دورة حياة (المدة الكاملة لتنفيذ البرنامج)- لا توجد أعباء تشغيلية طوال دورة حياته — فهو يختفي بمجرد تجميعه.
📝 تمارين
- الصعوبة ⭐: اقرأ جزءًا من كود Rust موجود (مثل المثال الوارد في الدرس 9) وحدد الدوال التي تتجنب الحاجة إلى التوضيح اليدوي بفضل قاعدة حذف مدة الصلاحية.
- الصعوبة ⭐⭐⭐: اكتب دالة
fn shortest<'a>(x: &'a str, y: &'a str) -> &'a strتُرجع أقصر سلسلتين. فيmain، أنشئ متغيرات ذات فترات صلاحية مختلفة لاستدعاء هذه الدالة. - الصعوبة ⭐⭐⭐⭐: عرّف بنية
Book<'a>تحتوي على حقلtitleمن النوع&'a str. اكتب كتلةimplتُنفِّذ طريقةfn first_word(&self) -> &strتُرجع الكلمة الأولى من العنوان. اشرح كيفية استخدامها فيmain.