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

Menerima Argumen Command Line

Mari kita bikin project baru dengan cargo new, seperti biasa. Kita bakal menamakan project kita minigrep buat ngebedain dari alat grep yang mungkin udah ada di sistem kita.

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

Tugas pertama adalah bikin minigrep menerima dua argumen command line-nya: path file dan sebuah string yang mau dicari. Yakni, kita mau bisa menjalankan program kita pakai cargo run, dengan dua tanda hubung (hyphens) buat menandakan argumen berikutnya itu buat program kita bukannya buat cargo, sebuah string buat dicari, dan path ke file tempat nyarinya, kayak gini:

$ cargo run -- searchstring example-filename.txt

Saat ini, program yang di-generate sama cargo new belum bisa memproses argumen yang kita berikan. Beberapa libraries yang udah ada di crates.io bisa bantu nulis program yang menerima argumen command line, tapi karena kita baru aja belajar konsep ini, mari kita mengimplementasikan kemampuan ini sendiri.

Membaca Nilai Argumen

Biar minigrep bisa ngebaca nilai argumen command line yang kita masukkan ke dalamnya, kita butuh fungsi std::env::args yang disediakan di standard library Rust. Fungsi ini mengembalikan sebuah iterator dari argumen-argumen command line yang diberikan ke minigrep. Kita bakal membahas iterators secara lengkap di Bab 13. Buat sekarang, kita cuma perlu tahu dua detail soal iterators: iterators menghasilkan serangkaian nilai, dan kita bisa memanggil method collect pada sebuah iterator buat mengubahnya jadi sebuah collection (koleksi), kayak vector misalnya, yang berisi semua elemen yang dihasilkan sama iterator tersebut.

Kode di Listing 12-1 memungkinkan program minigrep kita ngebaca argumen command line apa pun yang diberikan ke dia, terus mengumpulkan nilai-nilainya ke dalam sebuah vector.

Filename: src/main.rs
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    dbg!(args);
}
Listing 12-1: Mengumpulkan argumen command line ke dalam sebuah vector dan mencetaknya

Pertama kita bawa modul std::env ke dalam scope pakai statement use biar kita bisa memakai fungsi args-nya. Perhatikan kalau fungsi std::env::args ini bersarang (nested) di dua level modul. Kayak yang udah kita bahas di Bab 7, di kasus di mana fungsi yang mau kita pakai bersarang di lebih dari satu modul, kita memilih buat membawa induk modulnya ke dalam scope ketimbang fungsinya langsung. Dengan melakukan hal itu, kita bisa dengan gampang memakai fungsi lain dari std::env. Hal ini juga mengurangi kebingungan (ambiguity) dibandingkan menambahkan use std::env::args lalu memanggil fungsinya dengan args doang, karena args gampang sekali disangka fungsi yang didefinisikan di modul saat ini.

Fungsi args dan Unicode Tidak Valid

Perhatikan bahwa std::env::args bakal panic kalau ada argumen yang mengandung karakter Unicode yang tidak valid. Kalau program kita perlu menerima argumen yang mengandung Unicode tidak valid, pakai std::env::args_os sebagai gantinya. Fungsi tersebut mengembalikan iterator yang menghasilkan nilai OsString bukannya nilai String. Kita memilih memakai std::env::args di sini biar simpel karena nilai OsString itu beda-beda di setiap platform dan lebih rumit buat dikerjain dibanding nilai String.

Di baris pertama dari main, kita memanggil env::args, lalu kita langsung memakai collect buat mengubah iterator itu jadi vector yang berisi semua nilai yang dihasilkan sama iterator-nya. Kita bisa memakai fungsi collect buat bikin berbagai jenis koleksi, jadi kita harus secara eksplisit menganotasi tipe dari args buat menentukan kalau kita mau sebuah vector berisi strings. Walaupun kita jarang sekali perlu menganotasi tipe di Rust, collect adalah salah satu fungsi yang sering sekali butuh anotasi karena Rust tidak bisa menebak jenis koleksi apa yang kita mau.

Terakhir, kita mencetak vector-nya pakai debug macro. Mari kita coba jalankan kodenya dulu tanpa argumen lalu dengan dua argumen:

$ 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",
]

Perhatikan kalau nilai pertama di vector itu adalah "target/debug/minigrep", yaitu nama dari binary kita. Ini sesuai sama perilaku daftar argumen di bahasa C, membiarkan program memakai nama yang digunakan saat mereka dipanggil dalam eksekusinya. Sering kali praktis punya akses ke nama program kalau kita mau mencetaknya di dalam pesan atau ngubah perilaku program berdasarkan alias command line apa yang dipakai buat memanggil programnya. Tapi buat tujuan bab ini, kita abaikan saja itu dan cuma nyimpen dua argumen yang kita butuhin.

Menyimpan Nilai Argumen di dalam Variabel

Program kita saat ini sudah bisa mengakses nilai yang ditentukan sebagai argumen command line. Sekarang kita perlu menyimpan nilai dari kedua argumen itu di dalam variabel supaya kita bisa memakai nilainya di seluruh bagian program. Kita lakuin itu di Listing 12-2.

Filename: src/main.rs
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}");
}
Listing 12-2: Membuat variabel buat menampung argumen query (pencarian) dan argumen path (jalur) file

Seperti yang kita lihat saat kita mencetak vector tadi, nama program menempati nilai pertama di vector pada args[0], jadi kita mulai ngambil argumennya di indeks 1. Argumen pertama yang diterima minigrep adalah string yang lagi kita cari, jadi kita menaruh referensi ke argumen pertama tersebut di variabel query. Argumen kedua bakal jadi path file, jadi kita menaruh referensi ke argumen kedua tersebut di variabel file_path.

Kita sementara mencetak nilai dari variabel-variabel ini buat membuktikan kalau kodenya berjalan sesuai keinginan kita. Mari kita jalankan program ini lagi dengan argumen test dan 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

Mantap, programnya jalan! Nilai-nilai argumen yang kita butuhkan sudah disimpan ke dalam variabel yang tepat. Nanti kita bakal menambahkan sedikit error handling (penanganan error) buat menangani beberapa potensi situasi yang keliru, misalnya saat user tidak memberikan argumen apa pun; buat sekarang, kita abaikan dulu situasi itu dan lanjut bekerja menambahkan kemampuan membaca file.