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.
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
dbg!(args);
}
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.
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}");
}
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.