Как сопоставить строку String со строковыми литералами в Rust через match?
Проблема: String vs &str
В Rust String — это владеющая строка (heap-allocated), а строковый литерал "start" — это &str (ссылка на строковый срез). Компилятор не позволяет напрямую сравнивать их в match из-за несоответствия типов. Есть несколько идиоматичных решений.
Решение 1: Использование as_str() (рекомендуется)
Самый простой и читаемый способ — вызвать метод .as_str() у String, который возвращает &str. После этого вы можете сопоставлять с литералами:
fn main() {
let command = String::from("start");
match command.as_str() {
"start" => println!("Запуск!"),
"stop" => println!("Остановка!"),
_ => println!("Неизвестная команда"),
}
}
Преимущества:
- Минимальные изменения кода
- Нет копирования данных —
as_str()просто заимствует содержимоеString - Читаемость и идиоматичность
Решение 2: Заимствование через & и дереференс
Вы можете взять ссылку на String и использовать дереференс (*) или автоматическое приведение (deref coercion):
fn main() {
let command = String::from("start");
match &*command { // или просто &command
"start" => println!("Запуск!"),
"stop" => println!("Остановка!"),
_ => println!("Неизвестная команда"),
}
}
&*command работает так: * разыменовывает String в str (срез), а & берёт ссылку на него, получая &str. На практике можно писать просто &command — сработает deref coercion.
Решение 3: Преобразование литералов в String
Можно преобразовать каждый литерал в String с помощью .to_string():
fn main() {
let command = String::from("start");
match command {
s if s == "start".to_string() => println!("Запуск!"),
s if s == "stop".to_string() => println!("Остановка!"),
_ => println!("Неизвестная команда"),
}
}
Но этот подход менее эффективен, так как каждый раз создаётся новая String на куче. Используйте его только если вам нужно владение строкой в ветке match.
Решение 4: Использование matches! макроса (Rust 2018+)
Если у вас много вариантов, можно использовать макрос matches!:
fn main() {
let command = String::from("start");
if matches!(command.as_str(), "start" | "stop") {
println!("Команда распознана");
} else {
println!("Неизвестная команда");
}
}
Это удобно для простых проверок, но не для полноценного ветвления с разными действиями.
Рекомендация
Для большинства случаев используйте command.as_str() — это идиоматично, эффективно и читаемо. Если вы работаете с большим количеством строк или производительность критична, рассмотрите использование enum для команд вместо строк.
Альтернативный подход: Использование enum вместо строк
Если вы пишете парсер команд, который будет использоваться часто, лучше отказаться от String в пользу перечисления (enum). Это даёт безопасность типов и ускоряет сопоставление:
enum Command {
Start,
Stop,
Unknown(String),
}
fn parse_command(input: &str) -> Command {
match input {
"start" => Command::Start,
"stop" => Command::Stop,
other => Command::Unknown(other.to_string()),
}
}
fn main() {
let command = parse_command("start");
match command {
Command::Start => println!("Запуск!"),
Command::Stop => println!("Остановка!"),
Command::Unknown(cmd) => println!("Неизвестная команда: {}", cmd),
}
}
Преимущества:
- Сопоставление происходит по целочисленным дискриминантам, а не по строкам — быстрее
- Компилятор проверяет, что вы обработали все варианты
- Код становится самодокументированным
Этот подход особенно полезен, когда команд много или они сложные. Если вам нужно сохранить исходную строку (например, для логирования), можно добавить поле в enum.
Ответов пока нет.
Войдите чтобы оставить ответ