Rust: كيفية مقارنة String مع حرفية السلسلة النصية (match)

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا لا يسمح Rust بمقارنة String مع "مرحباً" ببساطة؟

\n\n

إذا كنت قد بدأت للتو في تعلم Rust وواجهت الحاجة لمطابقة متغير من النوع String مع حرفية سلسلة نصية مثل "hello" أو "world"، فمن المحتمل أنك رأيت خطأً غريباً من المترجم. تكتب شيئاً مثل:

\n\n
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
\n\n

عندما تكتب match user_input { "start" => ... }، فأنت تحاول مقارنة String مع &str. يقول المترجم: "لا أعرف كيف أطابقهما مباشرة، الأنواع مختلفة!". وهذا جيد - Rust يحميك من التحويلات الضمنية التي قد تؤدي إلى فقدان الأداء أو أخطاء خفية.

\n\n

ولكن ماذا نفعل إذاً؟ هناك ثلاثة حلول بسيطة وأنيقة.

\n\n

الحل 1: الاقتراض عبر as_str()

\n\n

الطريقة الأكثر مباشرة ووضوحاً هي الحصول على شريحة سلسلة نصية (&str) من String باستخدام الدالة as_str(). هذه الدالة ترجع مرجعاً لمحتوى السلسلة النصية دون نسخها. يبدو الأمر كالتالي:

\n\n
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

السلبيات: تحتاج إلى تذكر استدعاء الدالة. لكن هذا أمر بسيط.

\n\n

الحل 2: المقارنة في شرط if

\n\n

إذا لم تكن بحاجة إلى كل قوة match، يمكنك استخدام if العادي مع الدالة eq() أو العامل ==. يستطيع Rust مقارنة String و &str من خلال trait PartialEq، والذي تم تنفيذه لهذا الزوج من الأنواع.

\n\n
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 لا يوجد مثل هذا التحويل - فهو يتطلب تطابقاً دقيقاً.

المدونات

توصيات الكتب