كيفية تحديث قيمة في mutable HashMap في Rust: من المبتدئ إلى المحترف
\n\nهل سبق لك أن واجهت موقفًا تحتاج فيه إلى تحديث قيمة في HashMap في Rust، لكن المترجم يرفض بعناد تمرير الكود الخاص بك؟ إذا كان الأمر كذلك - فأنت لست وحدك. سؤال "How can I update a value in a mutable HashMap?" على Stack Overflow حصد آلاف المشاهدات، وهذا يعني أنه حتى المطورين ذوي الخبرة يخلطون أحيانًا في تفاصيل العمل مع هذه المجموعة.
في هذه المقالة، سنشرح جميع طرق تحديث القيم في HashMap في Rust: من insert الأساسي إلى entry().and_modify() المتقدم. ستتعلم كيفية تعديل البيانات بشكل صحيح، وتجنب الأخطاء الشائعة، وكتابة كود اصطلاحي. هيا بنا!
لماذا تحديث القيمة في HashMap ليس مجرد "خذ وغيّر"؟
\n\nعلى عكس العديد من اللغات، تجبرك Rust على العمل بشكل صريح مع الملكية والاقتراض. عندما تريد تحديث قيمة في HashMap، تحتاج إلى حل ثلاث مهام:
- \n
- التحقق من وجود المفتاح — هل توجد قيمة بالفعل؟ \n
- تعديل القيمة — إذا كان المفتاح موجودًا، قم بتحديثه \n
- إدراج قيمة جديدة — إذا لم يكن المفتاح موجودًا، أضفه \n
يبدو الأمر بسيطًا؟ دعنا نلقي نظرة على الكود.
\n\nالطريقة 1: insert الكلاسيكي (لكن مع خدعة)
\n\nالطريقة الأكثر مباشرة هي استخدام insert. يقوم بإعادة كتابة القيمة إذا كان المفتاح موجودًا، أو إدراج زوج جديد إذا لم يكن المفتاح موجودًا.
use std::collections::HashMap;\n\nlet mut scores = HashMap::new();\nscores.insert(String::from("Alice"), 10);\nscores.insert(String::from("Bob"), 20);\n\n// تحديث قيمة Alice\nscores.insert(String::from("Alice"), 15);\n\nprintln!("{:?}", scores); // {"Alice": 15, "Bob": 20}\n\nهل يعمل؟ نعم. لكن هناك فارق بسيط: insert يُرجع Option<V> — القيمة القديمة، إذا كانت موجودة. هذا مفيد إذا كنت تريد الاحتفاظ بالقيمة السابقة. لكن ماذا لو كنت بحاجة إلى تحديث القيمة بناءً على القديمة؟ على سبيل المثال، زيادة العداد بمقدار 1؟
// نريد زيادة نقاط Alice بمقدار 1\nif let Some(score) = scores.get_mut("Alice") {\n *score += 1;\n}\n\nهذا يعمل، لكنه يتطلب عمليتين: التحقق من الوجود والتعديل. وماذا لو لم يكن المفتاح موجودًا؟ سيتعين عليك إجراء الإدراج بشكل منفصل. ليس أنيقًا جدًا.
\n\nالطريقة 2: Entry API — ملك التحديثات
\n\nفي Rust، هناك أداة قوية — Entry API. تتيح لك في تعبير واحد التحقق من وجود المفتاح، وتحديث القيمة أو إدراجها. هذه هي الطريقة الاصطلاحية للعمل مع HashMap.
use std::collections::HashMap;\n\nlet mut word_count = HashMap::new();\n\n// عد الكلمات في الجملة\nfor word in "hello world hello Rust".split_whitespace() {\n let count = word_count.entry(word.to_string()).or_insert(0);\n *count += 1;\n}\n\nprintln!("{:?}", word_count);\n// {"hello": 2, "world": 1, "Rust": 1}\n\nدعنا نحلل السحر:
\n- \n
entry(key)— ينشئ إدخالاً للمفتاح \n or_insert(0)— إذا لم يكن المفتاح موجودًا، يُدرج القيمة 0 ويُرجع مرجعًا قابلًا للتعديل إليها \n *count += 1— نقوم بإلغاء الإشارة إلى المرجع وزيادة القيمة \n
هكذا! سطر واحد — وقد حللنا المشكلة. لكن هذا ليس كل شيء.
\n\nالطريقة 3: and_modify — عندما تحتاج فقط إلى التحديث
\n\nماذا لو كنت تريد تحديث القيمة فقط إذا كان المفتاح موجودًا، وإذا لم يكن موجودًا — لا تفعل شيئًا؟ أو إدراج قيمة مختلفة؟ لهذا يوجد and_modify و or_insert في سلسلة.
use std::collections::HashMap;\n\nlet mut inventory = HashMap::new();\ninventory.insert("apples".to_string(), 10);\ninventory.insert("bananas".to_string(), 5);\n\n// زيادة عدد التفاح بمقدار 1 إذا كان موجودًا\ninventory.entry("apples".to_string())\n .and_modify(|