expected `String`, found `&str`

error[E0308]: mismatched types — expected `String`, found `&str`

rustc 1.97.1

العَرَض

error[E0308]: mismatched types
 --> src/main.rs:2:21
  |
2 |     let s: String = "hello";
  |            ------   ^^^^^^^ expected `String`, found `&str`
  |            |
  |            expected due to this
  |
help: try using a conversion method
  |
2 |     let s: String = "hello".to_string();
  |                            ++++++++++++

rustc 1.97.1 (8bab26f4f 2026-07-14)

ونفس الخطأ بشكل تاني، لما تمرر نص لدالة:

error[E0308]: mismatched types
 --> src/main.rs:6:11
  |
6 |     greet("world");
  |     ----- ^^^^^^^ expected `String`, found `&str`
  |     |
  |     arguments to this function are incorrect
  |
note: function defined here
 --> src/main.rs:1:4
  |
1 | fn greet(name: String) {
  |    ^^^^^ ------------
help: try using a conversion method
  |
6 |     greet("world".to_string());
  |                  ++++++++++++

rustc 1.97.1 (8bab26f4f 2026-07-14)

السبب

فيه نوعين للنص في Rust، وهما مش نسختين من نفس الحاجة — دول حاجتين مختلفتين فعلاً في الذاكرة.

"hello" اللي بتكتبها كده في الكود اسمها string literal، ونوعها &str. الحروف بتاعتها مكتوبة جوه الملف التنفيذي نفسه وقت الترجمة، فهي موجودة طول عمر البرنامج، ومينفعش تتغير، ومحدش بيملكها. اللي بيتحط على الستاك حقلين بس — عنوان وطول، 16 بايت.

String حاجة تانية خالص: نص محجوز على الهيب، بيملكه متغير، ينفع يكبر ويتغير. اللي بيتحط على الستاك تلات حقول — عنوان وطول وسعة، 24 بايت.

الكومبايلر مش بيحوّل بينهم لوحده، لأن التحويل مش مجاني: String من &str معناه حجز على الهيب ونسخ للحروف. لو ده حصل ضمني، كنت هتدفع التكلفة دي من غير ما تشوفها في الكود.

الحل

من &str لـ String — دي هي اللي بتكلّف، لأنها بتحجز:

fn main() {
    let a: String = "hello".to_string();
    let b: String = String::from("hello");
    let c: String = "hello".into();

    println!("{a} {b} {c}");
}
افتح في Playground

التلاتة بيعملوا نفس الحاجة بالظبط. String::from هي الأوضح للقارئ الجديد.

من String لـ &str — دي مجانية، مفيش نسخ:

fn takes_str(s: &str) {
    println!("{s}");
}

fn main() {
    let owned = String::from("hello");

    takes_str(&owned);
    takes_str("literal");
}
افتح في Playground

القاعدة العملية

في الباراميترات، خد &str مش String — إلا لو الدالة محتاجة تملك النص فعلاً.

الدالة اللي بتاخد &str تقدر تستقبل الاتنين: literal على طول، وString بـ & قدامها. الدالة اللي بتاخد String بتجبر كل حد بينده عليها إنه يحجز على الهيب، حتى لو مكانش محتاج.

fn good(name: &str) {}      // بتقبل "world" و &owned
fn limiting(name: String) {} // بتقبل String بس

خد بالك إن رسالة الخطأ نفسها في المثال التاني فوق بتلمّح لده: بتقولك this parameter takes ownership of the value وبتقترح تغيّر نوع الباراميتر.

ليه بيحصل ده

الفرق ده مش تعقيد زيادة — هو نفس الفرق بين الستاك والهيب اللي كل حاجة في Rust مبنية عليه. &str استعارة لحروف موجودة في مكان تاني، وString ملكية لحروف على الهيب.

الدرس اللي بيشرح ده بالتفصيل ومعاه مرئي هو الستاك والهيب.

النقاش

لو عندك سؤال أو لقيت غلطة في الصفحة دي، اكتبها فيالنقاشات على GitHub. كل صفحة ليها موضوعها الخاص.