Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Прийняття аргументів командного рядка

Давайте створимо новий пакет за допомогою, як завжди, cargo new. Назвемо наш пакет minigrep, щоб відрізнити його від інструмента grep, який, можливо, уже є у вашій системі:

$ cargo new minigrep
     Created binary (application) `minigrep` project
$ cd minigrep

Перше завдання — змусити minigrep приймати свої два аргументи командного рядка: шлях до файлу і рядок для пошуку. Тобто ми хочемо мати змогу запускати нашу програму за допомогою cargo run, двох дефісів, щоб позначити, що наступні аргументи призначені для нашої програми, а не для cargo, рядка для пошуку і шляху до файлу, в якому виконувати пошук, ось так:

$ cargo run -- searchstring example-filename.txt

Наразі програма, згенерована cargo new, не може обробляти аргументи, які ми їй передаємо. Деякі наявні бібліотеки на Crates.io можуть допомогти з написанням програми, що приймає аргументи командного рядка, але оскільки ви лише вивчаєте цю концепцію, давайте реалізуємо цю можливість самостійно.

Читання значень аргументів

Щоб minigrep міг читати значення аргументів командного рядка, які ми передаємо йому, нам знадобиться функція std::env::args, надана в стандартній бібліотеці Rust. Ця функція повертає ітератор аргументів командного рядка, переданих minigrep. Ми повністю розглянемо ітератори в Розділі 13. Наразі вам потрібно знати лише дві деталі про ітератори: ітератори створюють послідовність значень, і ми можемо викликати метод collect на ітераторі, щоб перетворити його на колекцію, таку як вектор, яка містить усі елементи, що їх створює ітератор.

Код у Переліку 12-1 дає змогу вашій програмі minigrep читати будь-які аргументи командного рядка, передані їй, а потім збирати значення у вектор.

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    dbg!(args);
}

Спочатку ми вводимо модуль std::env в область видимості за допомогою оператора use, щоб ми могли використовувати його функцію args. Зверніть увагу, що функція std::env::args вкладена на два рівні модулів. Як ми обговорювали в Розділі 7, у випадках, коли потрібна функція вкладена більш ніж в одному модулі, ми вирішили вводити в область видимості батьківський модуль, а не саму функцію. Роблячи так, ми можемо легко використовувати інші функції з std::env. Це також менш неоднозначно, ніж додати use std::env::args, а потім викликати функцію просто як args, тому що args легко можна помилково сплутати з функцією, визначеною в поточному модулі.

Функція args і неприпустимий Unicode

Зверніть увагу, що std::env::args призведе до паніки, якщо будь-який аргумент містить неприпустимий Unicode. Якщо ваша програма повинна приймати аргументи, що містять неприпустимий Unicode, натомість використовуйте std::env::args_os. Ця функція повертає ітератор, який створює значення OsString замість значень String. Ми вирішили використовувати тут std::env::args для простоти, оскільки значення OsString відрізняються залежно від платформи і є складнішими у використанні, ніж значення String.

У першому рядку main ми викликаємо env::args, і одразу використовуємо collect, щоб перетворити ітератор на вектор, що містить усі значення, створені ітератором. Ми можемо використовувати функцію collect для створення багатьох видів колекцій, тому ми явно вказуємо тип args, щоб зазначити, що ми хочемо вектор рядків. Хоча вам дуже рідко потрібно вказувати типи в Rust, collect — це одна з функцій, для якої вам часто потрібно вказувати тип, тому що Rust не може вивести, який саме тип колекції ви хочете.

Нарешті, ми виводимо вектор за допомогою макроса налагодження. Давайте спробуємо запустити код спочатку без аргументів, а потім із двома аргументами:

$ cargo run
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running `target/debug/minigrep`
[src/main.rs:5:5] args = [
    "target/debug/minigrep",
]
$ cargo run -- needle haystack
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.57s
     Running `target/debug/minigrep needle haystack`
[src/main.rs:5:5] args = [
    "target/debug/minigrep",
    "needle",
    "haystack",
]

Зверніть увагу, що перше значення у векторі — "target/debug/minigrep", яке є назвою нашого бінарного файлу. Це відповідає поведінці списку аргументів у C, даючи програмам використовувати назву, під якою їх було викликано під час виконання. Часто зручно мати доступ до назви програми, якщо ви хочете виводити її в повідомленнях або змінювати поведінку програми залежно від того, який псевдонім командного рядка було використано для виклику програми. Але для цілей цієї глави ми проігноруємо це і збережемо лише два потрібні нам аргументи.

Збереження значень аргументів у змінних

Наразі програма може отримувати доступ до значень, заданих як аргументи командного рядка. Тепер нам потрібно зберегти значення двох аргументів у змінних, щоб ми могли використовувати ці значення в решті програми. Ми робимо це в Переліку 12-2.

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    let query = &args[1];
    let file_path = &args[2];

    println!("Searching for {query}");
    println!("In file {file_path}");
}

Як ми бачили, коли виводили вектор, назва програми займає перше значення у векторі в args[0], тому ми починаємо аргументи з індексу 1. Перший аргумент, який приймає minigrep, — це рядок, який ми шукаємо, тому ми поміщаємо посилання на перший аргумент у змінну query. Другий аргумент буде шляхом до файлу, тому ми поміщаємо посилання на другий аргумент у змінну file_path.

Ми тимчасово виводимо значення цих змінних, щоб довести, що код працює так, як ми задумали. Давайте запустимо цю програму знову з аргументами test і sample.txt:

$ cargo run -- test sample.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt

Чудово, програма працює! Значення аргументів, які нам потрібні, зберігаються у правильних змінних. Пізніше ми додамо обробку помилок, щоб впоратися з певними потенційно помилковими ситуаціями, наприклад, коли користувач не надає аргументів; наразі ми проігноруємо цю ситуацію і замість цього попрацюємо над додаванням можливостей читання файлів.