Struct menyimpan beberapa field yang saling terkait, dan mengaksesnya satu per satu lewat operator titik — point.x, point.y — cepat terasa bertele-tele ketika setiap field justru ingin kita masukkan ke variabel lokal. Tindakan menguraikan data menjadi komponennya lewat pola disebut destructuring, dan pola struct melakukannya sekali jalan: satu baris pola mengikat seluruh field ke variabel lokal. Chapter ini membahas empat bentuk penguraian struct — shorthand nama field, penggantian nama binding, pengabaian field dengan .., serta pola bersarang untuk struct di dalam struct — lengkap dengan dua galat kompilator yang paling sering muncul ketika jumlah field pada pola tidak sesuai.
Menguraikan Semua Field Sekaligus #
Bentuk paling ringkas memakai shorthand: nama field ditulis langsung di dalam pola, dan setiap nama tersebut sekaligus menjadi nama variabel lokal. Pola Point { x, y } adalah kependekan dari Point { x: x, y: y } — bagian di kiri titik dua adalah nama field pada struct, bagian di kanan adalah pola untuk nilai field tersebut. Ketika keduanya sama, Rust mengizinkan satu nama saja.
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 3, y: -7 };
let Point { x, y } = point;
println!("x = {}, y = {}", x, y);
}Output yang Dihasilkan:
x = 3, y = -7Mari kita bedah kode di atas:
- Baris 1–4 (
struct Point { ... }): structPointmemuat dua field bertipei32; definisi inilah yang menjadi acuan pola. - Baris 7 (
let point = Point { x: 3, y: -7 };): variabelpointdibuat dengan nilaixsebesar3danysebesar-7. - Baris 9 (
let Point { x, y } = point;): pola struct menguraikanpointmenjadi dua variabel lokalxdany; karenai32bertipeCopy, nilainya tersalin ke masing-masing variabel danpointtetap sah. - Baris 11 (
println!("x = {}, y = {}", x, y);): kedua variabel hasil uraian dipakai langsung tanpa menyentuhpointlagi.
Penguraian pada let bersifat wajib berhasil — pola struct yang menyebut semua field selalu cocok untuk nilai bertipe struct tersebut, sehingga tidak ada cabang gagal yang perlu ditangani.
Mengganti Nama Binding #
Nama field tidak selalu cocok dengan konteks lokal: struct Rectangle menyimpan width dan height, tetapi kode yang memproses layar mungkin ingin memakai nama pendek w dan h. Pola mendukung hal ini dengan bentuk field: binding — field tetap disebut menurut definisi struct, sedangkan variabel lokal memakai nama pilihan kita.
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let screen = Rectangle { width: 1280, height: 720 };
let Rectangle { width: w, height: h } = screen;
println!("Lebar {} x tinggi {}", w, h);
}Output yang Dihasilkan:
Lebar 1280 x tinggi 720Mari kita amati baris-baris kuncinya:
- Baris 7 (
let screen = ...): variabelscreenmenyimpan data layar dengan lebar1280dan tinggi720. - Baris 9 (
let Rectangle { width: w, height: h } = screen;): bagianwidth: wmenyatakan “fieldwidthdiuraikan ke variabelw”; pola yang sama berlaku untukheight: hsehingga variabel lokal bertukar nama tanpa mengubah struct. - Baris 11 (
println!("Lebar {} x tinggi {}", w, h);): keluaran memakai nama baruwdanhyang hasilnya sama dengan nilai field aslinya.
Menguji Nilai Field di Dalam Pola #
Posisi field pada pola tidak harus berupa binding — posisi tersebut dapat diisi pola lain seperti literal, sehingga satu pola struct sekaligus menguji isi field dan mengikat field sisanya. Kombinasi ini berguna pada match: arm pertama dapat menampung struct yang field x-nya tepat 0, arm berikutnya mengikat nilai sisanya. Perhatikan urutan arm dari yang paling ketat syaratnya:
struct Point {
x: i32,
y: i32,
}
fn main() {
let points = [
Point { x: 0, y: 0 },
Point { x: 0, y: 5 },
Point { x: 2, y: 0 },
Point { x: 1, y: 4 },
];
for point in points {
match point {
Point { x: 0, y: 0 } => println!("Titik asal."),
Point { x: 0, y } => println!("Di sumbu Y, tinggi {}.", y),
Point { x, y: 0 } => println!("Di sumbu X, jarak {}.", x),
Point { x, y } => println!("Titik bebas ({}, {}).", x, y),
}
}
}Output yang Dihasilkan:
Titik asal.
Di sumbu Y, tinggi 5.
Di sumbu X, jarak 2.
Titik bebas (1, 4).Mari kita bedah pencocokannya untuk setiap elemen:
- Baris 14 (
for point in points {): setiap elemen array dipindahkan ke variabelpointsatu per satu, lalu diperiksa olehmatch. - Baris 16 (
Point { x: 0, y: 0 } => ...): dua field diuji literal — pola hanya cocok bila kedua nilai tepat nol; elemen pertama(0, 0)memenuhinya sehingga tercetak “Titik asal.”. - Baris 17 (
Point { x: 0, y } => ...): fieldxdiuji literal0sedangkan fieldydiikat ke variabely; elemen(0, 5)lolos ujixdan mengikaty = 5. - Baris 18 (
Point { x, y: 0 } => ...): kebalikan arm sebelumnya —ydiuji0,xdiikat; elemen(2, 0)memenuhinya. - Baris 19 (
Point { x, y } => ...): arm murni binding menampung semua kombinasi tersisa seperti(1, 4)sekaligus melengkapi cakupan pola agarmatchexhaustive.
Urutan arm menentukan hasil: elemen (0, 0) juga cocok dengan pola baris 17, tetapi arm baris 16 berada di atas sehingga didahulukan. Apabila arm penampung dipindah ke urutan pertama, seluruh elemen akan jatuh ke arm tersebut.
Mengabaikan Field dengan .. #
Struct dengan banyak field membuat penulisan pola melelahkan ketika hanya satu dua field yang dibutuhkan. Pola .. menampung seluruh field yang tidak disebut tanpa mengikatnya. Satu catatan penting: field yang diabaikan tidak ikut berpindah — kepemilikan tetap berada pada variabel struct asal, sehingga field tersebut masih sah diakses lewat variabel lama.
struct Book {
title: String,
author: String,
pages: u32,
rating: u8,
}
fn main() {
let book = Book {
title: String::from("Belajar Rust untuk Pemula"),
author: String::from("Siti Rahma"),
pages: 320,
rating: 4,
};
let Book { title, rating, .. } = book;
println!("Judul: {}.", title);
println!("Rating: {} dari 5.", rating);
println!("Penulis: {}, {} halaman.", book.author, book.pages);
}Output yang Dihasilkan:
Judul: Belajar Rust untuk Pemula.
Rating: 4 dari 5.
Penulis: Siti Rahma, 320 halaman.Mari kita bedah baris per baris:
- Baris 16 (
let Book { title, rating, .. } = book;): pola hanya menyebuttitledanrating; pola..meloloskanauthordanpagestanpa mengikat keduanya. - Baris 18–19 (
println!("Judul: {}.", title); ...): fieldtitlebertipeStringyang tidakCopy, sehingga uraian ini memindahkan isinya ke variabel lokaltitle—book.titletidak dapat dipakai lagi. - Baris 20 (
println!("Penulis: {}, {} halaman.", book.author, book.pages);): field yang diabaikan..tetap dimilikibook, sehinggabook.authordanbook.pagesmasih sah diakses setelah penguraian.
Melupakan satu field tanpa .. langsung ditolak kompilator dengan galat E0027, sedangkan menuliskan field yang tidak ada pada struct memicu E0026. Kode berikut memperlihatkan keduanya secara berurutan:
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 3, y: -7 };
let Point { x } = point;
println!("x = {}", x);
}Pesan error kompilator:
error[E0027]: pattern does not mention field `y`
--> main.rs:9:9
|
9 | let Point { x } = point;
| ^^^^^^^^^^^ missing field `y`
|
help: include the missing field in the pattern
|
9 | let Point { x, y } = point;
| +++
help: if you don't care about this missing field, you can explicitly ignore it
|
9 | let Point { x, y: _ } = point;
| ++++++
help: or always ignore missing fields here
|
9 | let Point { x, .. } = point;
| ++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0027`.Pesan E0027 menawarkan tiga perbaikan sekaligus: melengkapi field y, mengabaikannya secara eksplisit dengan y: _, atau memakai ... Varian kesalahan yang berlawanan terjadi ketika pola menyebut field yang tidak pernah ada:
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 3, y: -7 };
let Point { x, y, z } = point;
println!("x = {}, y = {}, z = {}", x, y, z);
}Pesan error kompilator:
error[E0026]: struct `Point` does not have a field named `z`
--> main.rs:9:23
|
9 | let Point { x, y, z } = point;
| ^ struct `Point` does not have this field
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0026`.Galat E0026 biasanya muncul akibat salah ketik atau salah ingat definisi struct; kompilator menunjuk kolom tepat di nama field yang bermasalah.
Pola Struct Bersarang #
Field struct dapat berisi struct lain, dan pola dapat menggali lapisan tersebut sekaligus. Contoh berikut memeriksa garis yang tersusun dari dua titik: arm pertama khusus untuk garis yang berawal di titik asal, arm kedua menangani sisanya dengan penggantian nama agar keempat koordinat tidak bertabrakan nama.
struct Point {
x: i32,
y: i32,
}
struct Line {
start: Point,
end: Point,
}
fn main() {
let line = Line {
start: Point { x: 0, y: 0 },
end: Point { x: 6, y: 8 },
};
match line {
Line { start: Point { x: 0, y: 0 }, end: Point { x, y } } => {
println!("Garis dari titik asal ke ({}, {}).", x, y)
}
Line { start: Point { x, y }, end: Point { x: x2, y: y2 } } => {
println!("Garis dari ({}, {}) ke ({}, {}).", x, y, x2, y2)
}
}
}Output yang Dihasilkan:
Garis dari titik asal ke (6, 8).Mari kita uraikan kedua armnya:
- Baris 17 (
match line {): nilailinedicocokkan; karenaLinetidakCopy, nilai ini berpindah kematch. - Baris 18 (
Line { start: Point { x: 0, y: 0 }, end: Point { x, y } } => ...): fieldstartdiisi pola structPointyang menguji dua literal nol, sedangkan fieldenddiisi pola bindingPoint { x, y }— dua lapis pola dievaluasi dalam satu arm. - Baris 21 (
Line { start: Point { x, y }, end: Point { x: x2, y: y2 } } => ...): arm kedua mengikat keempat koordinat; namax2dany2dipilih lewat bentukx: x2agar berbeda darixdanytitik awal. - Baris 18 dan 21 (kedua arm): cakupan gabungan keduanya mencakup seluruh kemungkinan
Line, sehingga tidak dibutuhkan wildcard.
Kedalaman bersarang tidak dibatasi — struct dalam struct dalam struct dapat diuraikan terus sepanjang pola mengikuti bentuk tipe data.
Ringkasan #
Pola struct menguraikan seluruh field sekaligus: shorthand Point { x, y } mengikat field ke variabel bernama sama, bentuk width: w mengganti nama binding, posisi field dapat diisi literal untuk menguji isi, dan .. meloloskan field yang tidak dibutuhkan tanpa memindahkannya. Melupakan field memicu E0027, sedangkan menyebut field yang tidak ada memicu E0026 — keduanya lengkap dengan saran perbaikan dari kompilator. Pola juga dapat menggali struct bersarang lapis demi lapis. Chapter berikutnya menerapkan teknik yang sama pada enum, tempat setiap varian membawa bentuk data yang berbeda-beda.