Как получить аргументы командной строки в Rust?
Основной способ: std::env::args()
В Rust для доступа к аргументам командной строки используется функция std::env::args(). Она возвращает итератор, а не готовый вектор. Чтобы получить коллекцию строк, нужно собрать итератор в Vec<String>:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
// args[0] — имя программы
// args[1], args[2]... — переданные аргументы
println!("Программа: {}", args[0]);
if args.len() < 2 {
eprintln!("Ошибка: укажите имя файла");
eprintln!("Использование: {} <файл>", args[0]);
std::process::exit(1);
}
let filename = &args[1];
println!("Обрабатываю файл: {}", filename);
}
Важные моменты:
- Первый аргумент (индекс 0) — это всегда путь к исполняемому файлу программы.
- Аргументы начинаются с индекса 1.
- Функция
args()паникует, если аргументы содержат невалидный Unicode. Если нужна поддержка любых байтов, используйтеstd::env::args_os(), которая возвращаетOsString. - Для вывода ошибок используйте
eprintln!(stderr), а неprintln!(stdout). std::process::exit(1)завершает программу с кодом ошибки (ненулевое значение).
Парсинг флагов (--verbose, -v)
Для простых случаев можно разобрать аргументы вручную:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let mut input_file = String::new();
let mut verbose = false;
let mut output_file = String::new();
let mut i = 1;
while i < args.len() {
match args[i].as_str() {
"-i" | "--input" => {
i += 1;
if i < args.len() {
input_file = args[i].clone();
} else {
eprintln!("Ошибка: --input требует значение");
std::process::exit(1);
}
}
"-o" | "--output" => {
i += 1;
if i < args.len() {
output_file = args[i].clone();
} else {
eprintln!("Ошибка: --output требует значение");
std::process::exit(1);
}
}
"-v" | "--verbose" => verbose = true,
"-h" | "--help" => {
println!("Использование: {} [ОПЦИИ]", args[0]);
println!(" -i, --input <ФАЙЛ> Входной файл");
println!(" -o, --output <ФАЙЛ> Выходной файл");
println!(" -v, --verbose Подробный вывод");
println!(" -h, --help Эта справка");
std::process::exit(0);
}
_ => {
eprintln!("Неизвестный аргумент: {}", args[i]);
std::process::exit(1);
}
}
i += 1;
}
if verbose {
println!("Входной файл: {}", input_file);
println!("Выходной файл: {}", output_file);
}
}
Продвинутый парсинг с крейтом clap
Для серьёзных проектов лучше использовать готовую библиотеку clap. Добавьте в Cargo.toml:
[dependencies]
clap = { version = "4.5", features = ["derive"] }
Пример с декларативным парсингом:
use clap::Parser;
#[derive(Parser)]
#[command(name = "image-converter")]
#[command(about = "Конвертирует изображения", long_about = None)]
struct Cli {
/// Входной файл
#[arg(short = 'i', long = "input")]
input: String,
/// Выходной файл (опционально)
#[arg(short = 'o', long = "output")]
output: Option<String>,
/// Подробный вывод
#[arg(short = 'v', long = "verbose", default_value_t = false)]
verbose: bool,
}
fn main() {
let cli = Cli::parse();
println!("Вход: {}", cli.input);
if let Some(out) = cli.output {
println!("Выход: {}", out);
}
if cli.verbose {
println!("Режим подробного вывода включён");
}
}
clap автоматически генерирует справку (--help), проверяет обязательные аргументы и выводит понятные сообщения об ошибках. Это стандарт де-факто в экосистеме Rust для работы с CLI.
Альтернатива: использование std::env::args_os()
Если ваша программа должна работать с не-UTF8 именами файлов (например, в Windows или с файлами в кодировке CP1251), используйте args_os():
use std::env;
use std::ffi::OsString;
fn main() {
let args: Vec<OsString> = env::args_os().collect();
// Преобразование в &str может вернуть None для невалидного UTF-8
if let Some(program) = args[0].to_str() {
println!("Программа: {}", program);
}
// Для вывода на экран лучше использовать отладочный формат
for (i, arg) in args.iter().enumerate() {
println!("arg[{}]: {:?}", i, arg);
}
}
Также обратите внимание на крейт structopt (предшественник clap с derive-макросами) — он всё ещё используется в старых проектах. Современный выбор — clap версии 4.x с feature "derive".
Для минималистичных скриптов, где не нужны флаги, а только позиционные аргументы, можно использовать std::env::args().nth(1):
fn main() {
let filename = std::env::args().nth(1).unwrap_or_else(|| {
eprintln!("Usage: program <filename>");
std::process::exit(1);
});
println!("File: {}", filename);
}
Метод nth(1) возвращает Option<String> — первый аргумент после имени программы. Это удобно для быстрых утилит без внешних зависимостей.