Avatar ·

Ошибка в Rust: "expected a literal" при использовании println! — как исправить?

Привет! Я только начал изучать Rust и столкнулся с ошибкой, которую не могу понять. Пытаюсь вывести значение переменной через println!, но компилятор ругается. Вот мой код: ```rust fn main() { let name = "Alice"; println!("Hello, " + name + "!"); } ``` Компилятор выдаёт: ``` error: expected a literal --> src/main.rs:3:23 | 3 | println!("Hello, " + name + "!"); | ^ | help: format argument must be a string literal ``` Я ожидал, что строка склеится как в Python или JavaScript, но Rust явно не хочет так работать. Почему? Как правильно выводить переменные? Я пробовал убрать конкатенацию и писать просто `println!("Hello, name!")` — тогда выводится буквально "Hello, name!", а не значение переменной. Подскажите, пожалуйста, что я делаю не так? В документации нашёл упоминание про макросы, но не до конца понял. Нужен простой пример, как вывести строку с переменной. Заранее спасибо! <<>>

Почему возникает ошибка?

В Rust макрос println! — это не функция, а макрос, который работает на уровне синтаксического анализа. Он ожидает строковый литерал (строку в кавычках, известную на этапе компиляции) в качестве первого аргумента. Конкатенация строк через + внутри макроса не поддерживается, потому что макрос разбирает аргументы статически, а не во время выполнения.

Правильный синтаксис: плейсхолдеры

Вместо конкатенации используйте фигурные скобки {} как плейсхолдеры для значений. Вот как исправить ваш пример:

fn main() {
    let name = "Alice";
    println!("Hello, {}!", name);  // Выведет: Hello, Alice!
}

Фигурные скобки — это место, куда подставится значение переменной. Вы можете использовать несколько плейсхолдеров:

let x = 10;
let y = 20;
println!("x = {}, y = {}", x, y);  // x = 10, y = 20

Форматирование с индексами и именами

Если у вас много аргументов, можно указывать индексы:

println!("{1} is {0} years old", 25, "Bob");  // Bob is 25 years old

Или именованные параметры (только в Rust 1.58+):

println!("{name} is {age} years old", name = "Alice", age = 30);

Почему нельзя просто "склеивать"?

В отличие от динамических языков, Rust — статически типизированный. Макрос println! на этапе компиляции проверяет количество и типы аргументов. Конкатенация строк — это операция времени выполнения, которая не вписывается в эту модель. Если вам нужно собрать сложную строку, используйте format!:

let greeting = format!("Hello, {}!", name);
println!("{}", greeting);  // Тоже работает

Типичные ошибки начинающих

  • Забыли кавычки у литерала: println!("{}", name) — правильно, println!({}, name) — ошибка.
  • Пытаетесь использовать println! как обычную функцию: Не пишите println!("строка" + var).
  • Выводите саму переменную без плейсхолдера: println!(name) — не скомпилируется, так как макрос ждёт литерал.

Надеюсь, теперь стало понятнее! Rust требует строгого синтаксиса, но зато вы получаете гарантии безопасности и производительности на этапе компиляции.

<<>>

Альтернатива: использование макроса write! или форматирование через Display

Если вам нужно вывести данные не только в консоль, но и, например, в строку, используйте format! — он работает так же, как println!, но возвращает String:

let result = format!("Привет, {}! Сегодня {} число.", "Мир", 15);
println!("{}", result);  // Привет, Мир! Сегодня 15 число.

Для вывода в поток (файл, сокет) используйте макрос write!:

use std::io::Write;

fn main() {
    let mut output = Vec::new();
    write!(&mut output, "Значение: {}", 42).unwrap();
    println!("{:?}", output);  // Выведет байты строки "Значение: 42"
}

Что если нужно вывести тип, не реализующий Display?

Некоторые типы (например, срезы или кортежи) не поддерживают плейсхолдер {}. Используйте {:?} для отладки (требуется трейт Debug):

let numbers = vec![1, 2, 3];
println!("{:?}", numbers);  // [1, 2, 3]

Или {:#?} для красивого форматирования с отступами. Это удобно при отладке сложных структур.