Sejak Chapter pertama kita mengetik println!("{}") tanpa pernah membaca aturan mainnya secara utuh. Padahal di balik placeholder itu ada satu bahasa formatting mini yang sama untuk seluruh keluarganya: println!, print!, dan macro format! yang mengembalikan String alih-alih mencetak. Menguasai bahasa ini berarti kita bisa merangkai kalimat dari nilai apa pun — nama, harga, rata-rata, array nilai — tanpa merangkai + secara manual dan tanpa peduli apakah nilainya teks atau angka. Chapter ini membedah bahasa tersebut mulai dari placeholder posisi, argumen bernama, format debug, sampai presisi angka dan perataan kolom — semuanya dengan output yang benar-benar diuji.

format! dan Keluarga Macro yang Satu Bahasa #

Macro format! menerima pola yang persis sama dengan println!: sebuah string format berisi placeholder, diikuti nilai-nilai pengisinya. Bedanya, format! tidak mencetak apa pun — macro ini merakit String milik sendiri di heap dan menyerahkannya sebagai nilai kembalian. Karena itu hasilnya bisa disimpan di variabel, dikembalikan dari fungsi, atau terus diproses dengan perkakas dari Chapter-chapter sebelumnya.

Placeholder Posisi, Indeks, dan Nama #

Bentuk paling dasar adalah {} yang diisi berurutan sesuai urutan argumen. Ketika sebuah nilai perlu dipakai berkali-kali, {0} dan {1} merujuk argumen berdasarkan nomor urutnya mulai dari nol. Ketika keterbacaan pola menjadi penting, argumen bisa diberi nama seperti {nama} dan dikirim sebagai nama = nilai:

rust
fn main() {
    let name = "Budi Santoso";
    let city = "Bandung";
    println!("{} tinggal di {}", name, city);
    println!("{0} berasal dari {1}, dan {0} suka Rust", name, city);
    let info = format!(
        "{nama} lahir di {kota} pada tahun {tahun}",
        nama = "Siti Aminah",
        kota = "Surabaya",
        tahun = 1998
    );
    println!("{}", info);
}

Output yang Dihasilkan:

text
Budi Santoso tinggal di Bandung
Budi Santoso berasal dari Bandung, dan Budi Santoso suka Rust
Siti Aminah lahir di Surabaya pada tahun 1998

Mari kita bedah kode di atas baris per baris:

  1. Baris 4 (println!("{} tinggal di {}", name, city);): dua placeholder kosong diisi mengikuti urutan argumen — name ke {} pertama, city ke {} kedua.
  2. Baris 5 (println!("{0} berasal dari {1}, dan {0} suka Rust", name, city);): placeholder bernomor memungkinkan pemakaian berulang — {0} merujuk name dua kali tanpa mengirim argumen dua kali, dan {1} merujuk city.
  3. Baris 6-11 (let info = format!(...)): format! membangun String dari pola dengan tiga argumen bernama. Nama nama, kota, dan tahun membuat pola terbaca seperti kalimat yang sudah jadi — tidak perlu lagi menebak argumen mana mengisi placeholder mana.
  4. Baris 12 (println!("{}", info);): hasil format! adalah String biasa yang dicetak seperti teks lainnya.

Sejak Rust edisi 2021, ada bentuk yang lebih ringkas lagi: placeholder {product} dapat langsung menangkap variabel bernama sama dari scope sekitar tanpa argumen eksplisit — contohnya kita lihat di bagian akhir Chapter ini.

Format Debug dengan {:?} dan {:#?} #

Placeholder {} menampilkan nilai dalam format untuk pengguna akhir. Placeholder {:?} menampilkan nilai dalam format debug — tampilan teknis untuk keperluan inspeksi saat mengembangkan program. Pada teks, format debug membungkus kutip ganda dan memperlihatkan escape yang ada; pada array, format debug menampilkan strukturnya dengan kurung siku:

rust
fn main() {
    let nickname = "Budi \"Si Cepat\" Tan";
    println!("tampilan biasa: {}", nickname);
    println!("tampilan debug: {:?}", nickname);
    let scores = [90, 85, 78];
    println!("debug array: {:?}", scores);
    println!("debug cantik:\n{:#?}", scores);
}

Output yang Dihasilkan:

text
tampilan biasa: Budi "Si Cepat" Tan
tampilan debug: "Budi \"Si Cepat\" Tan"
debug array: [90, 85, 78]
debug cantik:
[
    90,
    85,
    78,
]

Mari kita telaah baris-barisnya:

  1. Baris 2 (let nickname = "Budi \"Si Cepat\" Tan";): teks berisi kutip ganda di dalamnya, ditulis dengan escape \" di kode sumber.
  2. Baris 3 (println!("tampilan biasa: {}", nickname);): format biasa menampilkan teks apa adanya — kutipnya muncul tanpa pelindung.
  3. Baris 4 (println!("tampilan debug: {:?}", nickname);): format debug mempertahankan representasi literalnya: dibungkus kutip dan tanda kutip di dalamnya muncul sebagai \". Inilah wujud teks yang bisa langsung disalin kembali ke kode.
  4. Baris 6 (println!("debug array: {:?}", scores);): array nilai tampil satu baris dalam kurung siku.
  5. Baris 7 (println!("debug cantik:\n{:#?}", scores);): variasi {:#?} adalah pretty debug — nilai ditata multi-baris dengan indentasi, jauh lebih terbaca untuk data besar. Escape \n di pola hanya menambah baris kosong sebelum bloknya.

Presisi Angka dan Perataan Teks #

Placeholder menerima spesifikasi setelah titik dua. Presisi {:.2} membatasi digit desimal (dengan pembulatan), lebar {:>10} menyediakan kolom 10 karakter yang diisi rata kanan, {:<10} rata kiri, {:^10} rata tengah. Keduanya bisa digabung — {:>10.2} berarti kolom 10 karakter, rata kanan, dua desimal. Untuk angka, {:07} mengisi nol di depan dan {:+} selalu menampilkan tanda:

rust
fn main() {
    let price = 12345.6789;
    println!("harga 2 desimal: {:.2}", price);
    println!("harga 0 desimal: {:.0}", price);
    println!("rata-rata: {:.3}", 87.5);
    println!("teks kanan : |{:>10}|", "kanan");
    println!("teks kiri  : |{:<10}|", "kiri");
    println!("teks tengah: |{:^10}|", "tengah");
    println!("angka kanan: |{:>10.2}|", price);
    println!("nol depan  : |{:07}|", 42);
    println!("tanda plus : |{:+}|", 7);
}

Output yang Dihasilkan:

text
harga 2 desimal: 12345.68
harga 0 desimal: 12346
rata-rata: 87.500
teks kanan : |     kanan|
teks kiri  : |kiri      |
teks tengah: |  tengah  |
angka kanan: |  12345.68|
nol depan  : |0000042|
tanda plus : |+7|

Bedah hasilnya baris per baris:

  1. Baris 3 ({:.2}): 12345.6789 dibulatkan menjadi dua desimal — 12345.68. Presisi membulatkan, bukan memotong mentah.
  2. Baris 4 ({:.0}): tanpa desimal, pembulatan menghasilkan 12346.
  3. Baris 5 ({:.3}): angka dengan desimal lebih sedikit diberi nol penggenap — 87.500 tampil utuh tiga desimal.
  4. Baris 6-8 ({:>10}, {:<10}, {:^10}): kolom 10 karakter diisi teks 5 karakter: lima spasi di depan untuk rata kanan, lima di belakang untuk rata kiri, dua-tiga spasi membagi untuk rata tengah. Karakter | di kiri-kanan hanya penanda batas kolom.
  5. Baris 9 ({:>10.2}): gabungan lebar dan presisi — 12345.68 (8 karakter) ditambah dua spasi penggenap kolom.
  6. Baris 10 ({:07}): lebar 7 dengan pengisi nol untuk angka 42 menjadi 0000042.
  7. Baris 11 ({:+}): tanda plus selalu dicetak — berguna menampilkan selisih atau perubahan nilai.

Kurung Kurawal Literal dan Penangkapan Variabel #

Dua hal terakhir menutup bahasa ini. Pertama, karena {} adalah penanda placeholder, kurung kurawal yang bermaksud literal harus ditulis ganda {{ dan }}. Kedua, placeholder bernama dapat menangkap variabel dari scope secara langsung — pola paling ringkas untuk merangkai kalimat:

rust
fn main() {
    println!("kurung kurawal: {{ dan }}");
    let json = format!("{{\"nama\": \"{}\"}}", "Budi");
    println!("{}", json);
    let product = "Kopi Gayo";
    let price = 25000.0;
    let message = format!("Produk {product} dijual Rp{price}");
    println!("{}", message);
}

Output yang Dihasilkan:

text
kurung kurawal: { dan }
{"nama": "Budi"}
Produk Kopi Gayo dijual Rp25000

Mari kita bedah kode di atas:

  1. Baris 2 (println!("kurung kurawal: {{ dan }}");): {{ dan }} masing-masing menjadi satu kurung kurawal literal di output.
  2. Baris 3-4 (let json = format!("{{\"nama\": \"{}\"}}", "Budi");): pola JSON dibangun dengan kurung literal di sekeliling dan satu placeholder di tengah — hasilnya teks JSON sah yang siap disimpan atau dikirim.
  3. Baris 6-8 (let product = ...; let price = ...;): dua variabel biasa dideklarasikan.
  4. Baris 9 (let message = format!("Produk {product} dijual Rp{price}");): placeholder {product} dan {price} menangkap variabel bernama sama tanpa perlu argumen product = product — bentuk paling ringkas, dan tetap tampil benar untuk angka 25000.0 yang dicetak sebagai 25000.

Ringkasan #

Bahasa formatting di Rust dipakai sama oleh println! dan format!: {} mengisi berurutan, {0} merujuk indeks, {nama} memakai argumen bernama atau menangkap variabel sejak edisi 2021. Spesifikasi setelah titik dua mengubah wujud hasil — {:?} dan {:#?} menampilkan format debug biasa dan multi-baris, {:.2} mengatur presisi, {:>10} mengatur lebar dan perataan, {:07} serta {:+} menyempurnakan angka. Kurung literal ditulis {{ }}. Gabungan format! dengan perkakas Chapter sebelumnya membuat kita mampu merakit laporan teks yang rapi; satu kebutuhan tersisa: teks yang penuh backslash, kutip, dan baris baru — raw string menyusul di Chapter terakhir Section ini.