شرائح Vec<T> في Rust: كيف تعمل ولماذا تحتاجها؟
\n\nإذا كنت قد كتبت بلغة Rust من قبل وتساءلت "How do I get a slice of a Vec<T> in Rust?" — فأنت لست وحدك. هذا أحد أكثر الأسئلة شيوعًا على Stack Overflow، وقد حصد آلاف المشاهدات. وليس من دون سبب: العمل مع المتجهات وشرائحها هو مهارة أساسية، لا يمكن تصور أي مشروع جاد في Rust بدونها.
\n\nفي هذه المقالة، لن نعرض فقط كيفية الحصول على شريحة. بل سنستعرض أفضل الممارسات لعام 2025، ونتعلم كيفية تجنب الأخطاء الشائعة، ونفهم لماذا تجعلنا Rust نفكر كثيرًا في الاقتراض. هيا بنا!
\n\nما هي الشريحة (slice) في Rust؟
\n\nقبل أن نحصل على شريحة، دعنا نفهم ما هي. الشريحة هي مرجع لتسلسل متصل من العناصر في الذاكرة. في Rust، يُرمز للشريحة بـ &[T]. إنها بنية بيانات غير مالكة — فهي فقط "تنظر" إلى جزء من البيانات التي يملكها شخص آخر (مثل متجه أو مصفوفة).
الميزة الرئيسية للشرائح هي الأمان. يضمن مترجم Rust أنك لن تتجاوز حدود الذاكرة، ونظام الاقتراض لن يسمح لك بتعديل البيانات أثناء وجود مراجع نشطة عليها.
\n\nلماذا لا تستخدم المتجه مباشرة؟
\n\nالمتجه (Vec<T>) هو بنية مالكة. إذا مررته إلى دالة، فإما أن تنقله (وتفقد الوصول إليه) أو تمرر مرجعًا. تمنحك الشرائح المرونة: يمكنك تمرير جزء فقط من البيانات دون نسخها أو فقدان السيطرة على المتجه الأصلي.
على سبيل المثال، تعمل مع مجموعة بيانات كبيرة وتريد تمرير جزء منها إلى دالة للتحليل. الشريحة هي الأداة المثالية: فهي خفيفة (كلمتان فقط: مؤشر وطول) وآمنة.
\n\nالطرق الرئيسية للحصول على شريحة من Vec<T>
\n\nدعنا ننتقل إلى التطبيق العملي. هناك عدة طرق للحصول على شريحة من متجه. سنستعرض الأكثر شيوعًا وملاءمة.
\n\n1. عامل الترقيم: &vec[start..end]
\n\nالطريقة الأكثر مباشرة هي استخدام عامل النطاق ..:
let vec = vec![1, 2, 3, 4, 5];\nlet slice: &[i32] = &vec[1..4]; // شريحة من الفهرس 1 إلى 4 (بدون تضمين 4)\nprintln!("{:?}", slice); // [2, 3, 4]\n\nتستخدم هذه الطريقة التحقق من الحدود في وقت التشغيل. إذا تجاوزت حدود المتجه، سيصاب البرنامج بالذعر (panic). هذا مقبول للأمثلة التعليمية، لكن في كود الإنتاج يجب توخي الحذر.
\n\nيمكنك استخدام أشكال مختلفة من النطاقات:
\n\n- \n
&vec[..]— المتجه بأكمله \n&vec[2..]— من الفهرس 2 إلى النهاية \n&vec[..3]— من البداية إلى الفهرس 3 (بدون تضمين) \n&vec[1..=3]— من 1 إلى 3 شاملًا \n
2. الطريقة .as_slice()
\n\nتعيد هذه الطريقة شريحة على المتجه بأكمله. إنها أكثر أمانًا من &vec[..] لأنها تعبر بوضوح عن النية:
let vec = vec![10, 20, 30];\nlet slice: &[i32] = vec.as_slice();\nprintln!("{:?}", slice); // [10, 20, 30]\n\nالطريقة as_slice() مفيدة بشكل خاص عندما تريد التأكيد على أنك تعمل مع شريحة وليس مع متجه. هذا يجعل الكود أكثر قابلية للقراءة والتوثيق الذاتي.
3. الحصول على شريحة قابلة للتعديل: as_mut_slice()
\n\nإذا كنت بحاجة لتعديل العناصر داخل الشريحة (ولكن ليس المتجه نفسه)، استخدم as_mut_slice():
let mut vec = vec![1, 2, 3];\nlet slice: &mut [i32] = vec.as_mut_slice();\nslice[0] = 100;\nprintln!("{:?}", vec); // [100, 2, 3]\n\nلاحظ: يجب تعريف المتجه كـ mut. كما لا يمكنك الحصول على شرائح غير قابلة للتعديل وقابلة للتعديل في نفس الوقت — فهذا ينتهك قواعد الاقتراض.