عادة ما تأتي الأشهر الأولى من رجل أعمال مبتدئ إلى رأس حول مفهوم العمر والحيازة. بعض الناس ينفصلون عن هذا ، ولكن بالنسبة لأولئك الذين تمكنوا من البقاء على قيد الحياة ، لم يعد هذا يبدو غير عادي أو خطأ. سوف أصف النقاط الرئيسية التي ، على ما يبدو ، ساعدت على التكيف بشكل أسرع وأفضل مع مفهوم العمر والممتلكات. 
بالطبع ، الرسالة الإخبارية الرسمية أكمل وأكثر تفصيلاً ، لكنها تتطلب أيضًا مزيدًا من الوقت والصبر لفهم واستيعاب جميع المعلومات بشكل كامل. حاولت تجنب عدد كبير من التفاصيل وتقديم كل شيء من أجل زيادة التعقيد ، في محاولة لجعل هذه المقالة في متناول أولئك الذين بدأوا للتو بمشاهدة الصدأ ، أو لم يفهموا حقًا اللحظات الأولية من لوحة الإعلانات الرسمية.
لقد جعلني أكتب أيضًا ، على سبيل المثال ، من monad ، يمكنك العثور على بعض المواد التدريبية الرسمية ، لكنها ليست مفهومة جيدًا دائمًا ، ولا ينشأ الفهم إلا بعد قراءة شيء مثل "مقدمة أخرى" حول هذا الموضوع.
العمر
نحتاج أولاً إلى الشعور بالراحة مع شيئين - نهاية الكتلة ونقل القيمة إلى كتلة أخرى. سنبدأ لاحقًا في تعقيده بإضافة "إقراض" و "قابلية للتغيير" و "قابلية خفية".
بادئ ذي بدء ، يتم تحديد العمر الافتراضي للقيمة من خلال الجزء التالي:
- بداية الحياة: خلق القيمة. هذا أمر شائع بالنسبة لمعظم لغات البرمجة ، لذلك لا يحمل أي حمل غير عادي.
- نهاية الحياة. هذا هو المكان الذي سيدعوه Rust تلقائيًا إلى المدمر وينسى القيمة. في كتلة النطاق ، سيحدث هذا في نهاية هذه الكتلة دون تحريك. إن التتبع العقلي لنهاية الحياة هو ، في رأيي ، مفتاح التفاعل الناجح مع المدقق.
سأضيف تفاصيل قد تكون في متناول اليد: إذا كان هناك العديد من القيم في النطاق ، فسوف يتم تدميرها بالترتيب العكسي للخليقة.
نقطة أخرى: سوف أقوم بإنشاء سلسلة ، لأنها لا تحتوي على علامة نسخ ، والقيم التي تحتوي على هذه العلامة لا تتحرك ولكن يتم نسخها ، والتي تعتبر عملية رخيصة إلى حد ما ، ولكنها تغير سلوك الحركة (وتسهل العمل مع الأنواع البدائية) ، ولكن أكثر على ذلك في وقت لاحق.
يمكن تشغيل الأمثلة هنا: https://play.rust-lang.org/
fn main() { {
مع كتلة بسيطة ، كل شيء بسيط نسبياً ، تحدث المرحلة التالية عندما نستخدم أشياء تبدو بسيطة مثل الوظائف والإغلاقات:
تتحرك
أضف مفهومًا مثل تحريك القيمة. بعبارة بسيطة ، تعني كلمة "نقل" أن الكتلة الحالية لم تعد مهتمة بمصير القيمة وأنها تنسى عنها ، ويتم نقل مصيرها إلى كتلة أخرى ، على سبيل المثال ، إلى وظيفة أخرى ، أو إلى إغلاق ، أو ببساطة إلى قيمة أخرى.
fn f<T: std::fmt::Display>(x: T) {
مع الاغلاقات.
من أجل إغلاق عملية نقل القيمة التي تم التقاطها إلى حظرها ، يتم استخدام نقل الكلمة الرئيسية ؛ إذا لم تكتب حركة ، فسيتم استعارة القيمة التي سأكتب عنها قريبًا.
fn main() { let a = "a".to_string();
يمكنك الانتقال إلى الوظيفة ومن الوظيفة أو إلى قيمة أخرى.
يوضح هذا المثال كيفية تتبع الطرق التي تتحرك بها القيم من أجل العيش في سلام مع المدقق المقترض.
fn f(x: String) -> String { x + " and x"
الإقراض
نقدم هذا المفهوم الجديد: على عكس الحركة ، يعني هذا أن الكتلة الحالية تحتفظ بالتحكم في القيمة ، فهي تسمح ببساطة للكتلة الأخرى باستخدام قيمتها.
وألاحظ أن الاقتراض يحدث أيضًا عند نهايته ، وهو أمر غير مهم في هذه الأمثلة ، ولكنه سينبثق في الفقرة التالية.
ملاحظة: لن أكتب عن كيفية تحديد العمر مباشرة في الوظيفة ، لأن الصدأ الحديث يفعل هذا تلقائيًا بشكل أفضل مما كان عليه في الأيام القديمة ، والكشف عن كل هذا هو بضع صفحات أخرى.
fn f(x: &String) {
مع إغلاق بالمثل:
fn main() { let mut a = "a".to_string();
في الواقع ، في معظم هذه الإنشاءات البسيطة ، يحتاج المستخدم فقط إلى تحديد المكان الذي يريد إنهاء عمر القيمة فيه: في نهاية الكتلة الحالية وإقراضها لبعض الوظائف ، أو ، إذا علمنا أننا لم نعد بحاجة إلى القيمة ، فقم بنقلها إلى الوظيفة في النهاية من خلالها سيتم تدميرها بنفسها ، كلما قمنا بتحرير الذاكرة بشكل أسرع ، لكن القيمة لن تكون متوفرة في الكتلة الحالية.
قابلية التبادل
في الراستا ، على سبيل المثال ، في kotlin ، هناك تقسيم إلى قيم قابلة للتغيير وغير مستقرة. لكن المشكلة تكمن في أن قابلية التحويل لها تأثير على الإقراض:
يمكنك استعارة قيمة غير مستقرة عدة مرات ، ويمكن استعارة قيمة قابلة للتغيير مرة واحدة فقط. لا يمكنك تحويل قيمة تم اقتراضها بالفعل من قبل.
مثال لا يرتبط بالمثال السابق ، عندما ينقذنا هذا المفهوم من المشاكل عن طريق حظر الإقراض المتغير وغير المستقر المتزامن:
fn main() { let mut a = "abc".to_string(); for x in a.chars() {
من الضروري بالفعل تخزين العديد من الحيل من أجل تلبية المطالب العادلة للراستا في معظم الأحيان. في المثال أعلاه ، أسهل طريقة هي استنساخ "a" -> سيحصل المستنسخ على قرض غير مستقر ، وليس مرتبطًا بـ "a" الأصلي.
for x in a.clone().chars() {
لكن من الأفضل أن أعود إلى أمثلةنا للحفاظ على الاتساق. نحتاج إلى تغيير "a" ولا يمكننا القيام بذلك.
fn main() { let mut a = "a".to_string();
طفرة خفية
من الناحية النظرية ، يمكن تمرير إغلاق لبعض الوظائف التي تعالج ، على سبيل المثال ، بشكل غير متزامن في سلسلة رسائل أخرى ، ومن ثم سنواجه مشاكل بالفعل ، ولكن في هذه الحالة ، يتم إعادة تأمين المدقق ، على الرغم من أن هذا لا يلغي حقيقة أننا بحاجة إلى الموافقة عليها بطريقة أو بأخرى .
خلاصة القول: نحتاج إلى اقتراضين متحورين ، لكن الصدأ لا يسمح إلا بشيء واحد ، لكن مخترعي الماكرة في الراستا يتوصلون إلى "طفرة خفية": RefCell.
RefCell - ما نلفه في RefCell - يعتبره النقطية أمرًا ضروريًا ، ومع ذلك ، وباستخدام دالة الاقتراض () يمكننا استخلاص ارتباط قابل للتغيير يمكن من خلاله تغيير القيمة ، ولكن هناك فارق بسيط مهم : لا يمكن الحصول على الرابط إلا عندما يتأكد RefCell في وقت التشغيل من عدم وجود آخرين قروض نشطة ، وإلا فإنه سوف يلقي الذعر ، أو إرجاع خطأ إذا تم استخدام try_borrow_mut (). أي هنا يعطي النمو كل المخاوف بشأن الإقراض لرعاية المستخدم ، ويجب عليه هو نفسه التأكد من أنه لا يستعير القيمة من عدة أماكن في وقت واحد.
use std::cell::RefCell; fn main() { let a = RefCell::new("a".to_string());
الصليب الأحمر وصلة مكافحة
هذا البناء مألوف في العديد من اللغات ، ويستخدم في الصدأ ، عندما ، على سبيل المثال ، لا يمكننا استعارة قيمة لسبب ما ، وهناك حاجة إلى وجود العديد من القيم المرجعية لقيمة واحدة واحدة. Rc ، كما يوحي الاسم ، هو مجرد عداد مرجعي يمتلك قيمة ، ويمكنه استعارة روابط غير مستقرة ، وحساب عددهم ، وبمجرد إعادة تعيين الرقم ، يدمر القيمة ونفسه. اتضح أن Rc يسمح ، كما كان ، بتوسيع العمر الافتراضي للقيمة الموجودة به.
سأضيف أن الصدأ يمكن أن يقوم تلقائيًا بالتعرف على الهياكل التي تم تعريفه لها ، مما يعني أنه للعمل مع Rc ، كقاعدة عامة ، لا تحتاج إلى أي استخراج إضافي للقيمة الداخلية ونحن نعمل فقط مع Rc كما هو الحال مع القيمة بداخلها.
هنا تم التفكير في مثال بسيط قليلاً ، دعنا نحاول محاكاة حقيقة أن الإغلاق من المثال أعلاه لا يريد أن يقبل & T أو & String ، لكنه يريد سلسلة فقط:
fn f(x: String) {
يمكن حل هذه المشكلة بسهولة إذا استطعنا تغيير الوظيفة إلى fn f(x: &String)
(أو & str) ، ولكن دعونا نتخيل أنه لسبب ما لا يمكننا استخدام &
نحن نستخدم rc
use std::rc::Rc; fn f(x: Rc<String>) {
سأضيف المثال الأخير ، لأن أحد أزواج الحاويات الأكثر شيوعًا التي يمكن العثور عليها هو Rc <RefCell>
use std::rc::Rc; use std::cell::RefCell; fn f(x: Rc<RefCell<String>>) { x.borrow_mut().push_str(" and x");
علاوة على ذلك ، سيكون من المنطقي نقل هذا البرنامج التعليمي إلى نسخة تماثلية آمنة من خيط Rc-Arc ومن ثم المتابعة حول Mutex ، لكنك لن تتحدث عن سلامة الخيط ومدقق المقترض في فقرة واحدة ، وليس من الواضح ما إذا كانت هناك حاجة إلى هذا النوع من المقالة على الإطلاق ، نظرًا لوجود خيط رسمي رسمي. لذلك أختتم.