لماذا لا يسمح Rust بمقارنة String مع "مرحباً" ببساطة؟
\n\nإذا كنت قد بدأت للتو في تعلم Rust وواجهت الحاجة لمطابقة متغير من النوع String مع حرفية سلسلة نصية مثل "hello" أو "world"، فمن المحتمل أنك رأيت خطأً غريباً من المترجم. تكتب شيئاً مثل:
let user_input = String::from("start");\nmatch user_input {\n "start" => println!("نبدأ!"),\n "stop" => println!("نتوقف!"),\n _ => println!("أمر غير معروف"),\n}\n\n\nويصدر المترجم شيئاً مثل: "expected struct `String`, found `&str`". هل هذا مألوف؟
\n\nعلى Stack Overflow، سؤال "How to match a String against string literals?" حصل على آلاف المشاهدات - إنه ألم حقيقي للمبتدئين. في هذه المقالة، سنشرح لماذا يحدث هذا، ونعرض ثلاث طرق بسيطة لحل المشكلة. ستتعلم كيف يعمل نظام الملكية والاقتراض في Rust عملياً، وستتوقف عن الخوف من أخطاء المترجم.
\n\nطبيعة التعارض: String مقابل &str
\n\nلفهم المشكلة، علينا تذكر الأساسيات. في Rust، هناك نوعان رئيسيان للعمل مع النص:
\n\n- \n
String- سلسلة نصية مالكة، مخزنة في الكومة. يمكن أن تتغير وتنمو وتنكمش. مثال:let s = String::from("hello");\n &str- شريحة سلسلة نصية. هي مرجع لجزء من سلسلة نصية (أو لحرفية سلسلة نصية مخزنة مباشرة في ملف البرنامج الثنائي). مثال:"hello"هي بالضبط&'static str. \n
عندما تكتب match user_input { "start" => ... }، فأنت تحاول مقارنة String مع &str. يقول المترجم: "لا أعرف كيف أطابقهما مباشرة، الأنواع مختلفة!". وهذا جيد - Rust يحميك من التحويلات الضمنية التي قد تؤدي إلى فقدان الأداء أو أخطاء خفية.
ولكن ماذا نفعل إذاً؟ هناك ثلاثة حلول بسيطة وأنيقة.
\n\nالحل 1: الاقتراض عبر as_str()
\n\nالطريقة الأكثر مباشرة ووضوحاً هي الحصول على شريحة سلسلة نصية (&str) من String باستخدام الدالة as_str(). هذه الدالة ترجع مرجعاً لمحتوى السلسلة النصية دون نسخها. يبدو الأمر كالتالي:
fn main() {\n let command = String::from("start");\n\n match command.as_str() {\n "start" => println!("بدء النظام..."),\n "stop" => println!("إيقاف..."),\n "pause" => println!("إيقاف مؤقت..."),\n _ => println!("أمر غير معروف: {}", command),\n }\n}\n\n\nلماذا يعمل هذا؟ as_str() ترجع &str، والذي يمكن الآن مقارنته مع الحرفيات النصية. يرى Rust أن كلا المعاملين من النوع &str، وتعمل مطابقة الأنماط بشكل مثالي.
الإيجابيات: البساطة، سهولة القراءة، بدون تكاليف إضافية (اقتراض وليس نسخ).
\n\nالسلبيات: تحتاج إلى تذكر استدعاء الدالة. لكن هذا أمر بسيط.
\n\nالحل 2: المقارنة في شرط if
\n\nإذا لم تكن بحاجة إلى كل قوة match، يمكنك استخدام if العادي مع الدالة eq() أو العامل ==. يستطيع Rust مقارنة String و &str من خلال trait PartialEq، والذي تم تنفيذه لهذا الزوج من الأنواع.
fn main() {\n let user_input = String::from("admin");\n\n if user_input == "admin" {\n println!("تم منح الوصول!");\n } else if user_input == "guest" {\n println!("وصول ضيف");\n } else {\n println!("اسم مستخدم غير صحيح");\n }\n}\n\n\nهذا يعمل لأن المترجم يقوم تلقائياً بتحويل String إلى &str عند المقارنة. لكن في match لا يوجد مثل هذا التحويل - فهو يتطلب تطابقاً دقيقاً.