Lewati ke isi
JRG++JRG++ v0.2.0

JRG 8 · Dasar JRG

Struct & Method

Membuat tipe datamu sendiri. Supaya "titik" tidak lagi berupa dua variabel yang kebetulan berdekatan, melainkan satu benda utuh.

tipeMasalahnya dulu

Bayangkan kamu menyimpan data siswa: nama, nilai, dan kehadiran. Tanpa struct, ketiganya jadi variabel terpisah — dan tidak ada yang mengikat mereka.

tanpa-struct.jrg
pub fn main() {
    let nama = "Sari"
    let nilai = 88
    let hadir = 95
    wok(nama, nilai, hadir)
}
Sari 88 95

Sekarang bayangkan ada 30 siswa. Struct menyelesaikan itu:

struct.jrg
struct Siswa {
    pub nama: string,
    pub nilai: int,
    pub hadir: int
}

pub fn main() {
    let s = Siswa { nama: "Sari", nilai: 88, hadir: 95 }
    wok(s)
    wok("Nama :", s.nama)
    wok("Nilai:", s.nilai)
}
Siswa {hadir: 95, nama: Sari, nilai: 88}
Nama : Sari
Nilai: 88

pub: field yang boleh dilihat dari luar

Perhatikan kata pub di depan setiap field tadi. Tanpa itu, field hanya bisa dibaca dari dalam method tipe itu sendiri — dan JRG menolak kalau kamu mencoba membacanya dari luar.

private.jrg
struct Dompet {
    saldo: int          // tanpa pub -> tertutup
}

pub fn main() {
    let d = Dompet { saldo: 50000 }
    wok(d.saldo)
}
error: field `saldo` di `Dompet` bersifat private — cuma bisa diakses dari dalam method tipe itu sendiri atau turunannya. Tambahkan `pub` di depan field itu di deklarasi struct kalau memang harus publik.
 --> private.jrg:7:9
  |
7 |     wok(d.saldo)
  |         ^^^^^^^
  |
lewat-method.jrg
struct Dompet { saldo: int }

impl Dompet {
    fn baru(awal: int) -> Dompet { return Dompet { saldo: awal } }
    fn lihat(self) -> int { return self.saldo }
    fn tarik(self, n: int) -> Dompet {
        if n > self.saldo { return self }      // aturannya di sini saja
        return Dompet { saldo: self.saldo - n }
    }
}

pub fn main() {
    var d = Dompet::baru(50000)
    d = d.tarik(20000)
    d = d.tarik(999999)      // ditolak diam-diam oleh aturannya
    wok("Saldo:", d.lihat())
}
Saldo: 30000

Method: fungsi milik sebuah tipe

Blok impl menempelkan fungsi ke sebuah struct. Di dalamnya, self berarti “benda yang sedang dipanggil”.

impl.jrg
struct Siswa {
    nama: string,
    nilai: int,
    hadir: int
}

impl Siswa {
    fn lulus(self) -> bool {
        return self.nilai >= 75 && self.hadir >= 80
    }
    fn ringkas(self) -> string {
        return self.nama + ": " + self.nilai + (self.lulus() $ " (lulus)" : " (ulang)")
    }
}

pub fn main() {
    let a = Siswa { nama: "Sari", nilai: 88, hadir: 95 }
    let b = Siswa { nama: "Budi", nilai: 60, hadir: 90 }
    wok(a.ringkas())
    wok(b.ringkas())
}
Sari: 88 (lulus)
Budi: 60 (ulang)

Pembuat (constructor)

Method tanpa self dipanggil lewat nama tipenya. Biasa dipakai untuk membuat isi awal.

konstruktor.jrg
struct Titik {
    x: float,
    y: float
}

impl Titik {
    fn baru(x: float, y: float) -> Titik {
        return Titik { x: x, y: y }
    }
    fn asal() -> Titik {
        return Titik { x: 0.0, y: 0.0 }
    }
    fn jarakKe(self, lain: Titik) -> float {
        let dx = self.x - lain.x
        let dy = self.y - lain.y
        return math::sqrt(dx * dx + dy * dy)
    }
}

pub fn main() {
    let p = Titik::baru(3.0, 4.0)
    wok("Jarak ke titik asal:", p.jarakKe(Titik::asal()))
}
Jarak ke titik asal: 5.0

Nilai bawaan untuk field

default.jrg
struct Pengaturan {
    pub judul: string = "Tanpa judul",
    pub lebar: int = 80,
    pub tinggi: int = 24
}

pub fn main() {
    let a = Pengaturan {}
    let b = Pengaturan { judul: "Laporan" }
    wok(a)
    wok(b)
}
Pengaturan {judul: Tanpa judul, lebar: 80, tinggi: 24}
Pengaturan {judul: Laporan, lebar: 80, tinggi: 24}

Nilai tidak pernah berbagi tempat

Kalau kamu menyalin sebuah struct ke nama lain, keduanya berdiri sendiri. Mengubah satu tidak menyentuh yang lain — ini aturan pokok JRG.

salinan.jrg
struct Kotak { pub isi: int }

pub fn main() {
    var a = Kotak { isi: 1 }
    var b = a          // salinan, bukan penunjuk yang sama
    b.isi = 99
    wok("a:", a.isi, " b:", b.isi)
}
a: 1  b: 99

Menampilkan struct dengan caramu sendiri

Kalau sebuah tipe punya method bernama to_string, semua tempat yang mencetak akan memakainya.

to-string.jrg
struct Uang { jumlah: int }

impl Uang {
    fn to_string(self) -> string {
        return "Rp" + self.jumlah
    }
}

pub fn main() {
    let harga = Uang { jumlah: 12500 }
    wok(harga)
    wok("Total:", harga)
}
Rp12500
Total: Rp12500

Kuis 1

Apa arti self di dalam blok impl?

Kuis 2

Sesudah var b = a lalu b.isi = 99, berapa a.isi?

Kuis 3

Bagaimana memanggil method yang tidak punya self?

Latihan mandiri

1. Buat struct Persegi dengan field sisi, dan method luas() serta keliling().

2. Tambahkan method lebihBesarDari(lain) yang menjawab true/false.

Jawaban 1: fn luas(self) -> int { return self.sisi * self.sisi }, keliling 4 * self.sisi.
Jawaban 2: fn lebihBesarDari(self, lain: Persegi) -> bool { return self.luas() > lain.luas() }.