Menu

Generic di Zero: Parameter Tipe pada Fungsi dan Shape

Cara kerja generic di Zero: mendeklarasikan parameter tipe pada fungsi dan shape, memanggil fungsi generic, dan pola alias tipe yang mengubah parameterisasi panjang menjadi nama yang rapi.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Kenapa Perlu Generic

Kamu akan cepat menemukan tipe yang seharusnya bisa dipakai untuk lebih dari satu tipe elemen. Sebuah "pasangan" berisi dua nilai tidak peduli apakah nilainya integer, string, atau shape buatan pengguna. Generic membuatmu bisa menulis tipe itu sekali lalu membuat instance-nya dengan tipe elemen apa pun yang dibutuhkan pemanggil.

Alternatifnya, yaitu menulis IntPair, StringPair, BytePair, dan seterusnya, cepat membosankan dan tidak bisa digabungkan dengan baik. Generic di Zero adalah alat standar untuk pekerjaan ini.

Fungsi Generic

Deklarasikan parameter tipe di dalam kurung sudut, di antara nama fungsi dan daftar parameter:

fun makePair<T, U>(left: T, right: U) -> Pair<T, U> {
    return Pair { left: left, right: right }
}

T dan U adalah tipe pengganti, dan pemanggillah yang menentukan tipenya.

Pemanggilan biasanya tidak perlu menulisnya, karena compiler menginferensinya dari tipe argumen:

let pair = makePair(40, 2_u8)

Di sini T diinferensi sebagai i32 (default untuk literal integer tanpa akhiran) dan U sebagai u8 (dari akhiran _u8). Binding hasilnya, pair, bertipe Pair<i32, u8>.

Kalau inferensi akan memilih tipe yang salah, misalnya karena literalnya ambigu, kamu bisa menetapkan parameternya di tempat pemanggilan:

let pair = makePair<u8, u8>(1, 2)

(Apakah sintaks pemanggilan dengan kurung sudut persis seperti di atas bisa berbeda di tiap versi Zero. Cek dokumentasi terbaru untuk penulisan yang tepat. Perilaku yang mendahulukan inferensi adalah bagian yang stabil.)

Shape Generic

Shape menerima parameter tipe dengan cara yang sama:

shape Pair<T, U> {
    left: T,
    right: U,
}

Tipe setiap field bisa menyebut parameter itu. Instance-nya menetapkan tipenya:

let intBytes: Pair<i32, u8> = Pair { left: 40, right: 2_u8 }
let words:    Pair<String, String> = Pair { left: "hi", right: "there" }

Contoh lengkap yang menggabungkan shape generic dan fungsi generic. Klik Jalankan untuk melihat inferensi bekerja:

Satu deklarasi shape generic, satu fungsi generic, dan satu tempat pemanggilan yang bertipe kuat. Tidak perlu boilerplate IntBytePair.

Alias Tipe

Saat tipe dengan parameter yang sama muncul berulang kali, beri tipe itu nama dengan type:

type BytePair = Pair<u8, u8>

Sekarang BytePair bisa dipakai bergantian dengan Pair<u8, u8> di mana pun kamu bisa menulis tipe:

Alias hanyalah fitur penamaan, dan tidak membuat tipe yang berbeda. Fungsi yang menerima BytePair akan tetap menerima nilai bertipe Pair<u8, u8> (dan sebaliknya).

Generic di Standard Library

Mekanisme yang sama menjadi dasar banyak bagian standard library. Beberapa yang akan kamu lihat di kode Zero sungguhan:

  • Maybe<T>: nilai opsional, berisi sebuah T atau tidak berisi apa-apa.
  • Span<T>: potongan pinjaman atas nilai-nilai T. Span<u8> adalah tampilan standar atas sebuah buffer byte.
  • ref<T> dan mutref<T>: tipe referensi eksplisit untuk kasus saat kamu perlu berbagi data tanpa menyalinnya.

Kamu tidak perlu mempelajari semuanya sekaligus. Inti dari generic adalah shape yang sama bisa dipakai untuk tipe elemen apa pun yang kamu punya.

Kapan Generic Bermanfaat (dan Kapan Tidak)

Pakai generic saat kamu mendapati dirimu menulis fungsi atau shape yang sama dua kali dengan tipe elemen yang berbeda. Pakai tipe konkret saat:

  • Logika fungsi hanya masuk akal untuk tipe tertentu (misalnya parser untuk String).
  • Kamu ingin tipenya muncul di pesan error agar debugging lebih mudah.
  • Karakteristik performanya bergantung pada ukuran tertentu di memori.

Biaya generic itu nyata: binary jadi lebih besar (setiap instance menghasilkan kode baru) dan waktu kompilasi sedikit lebih lama. Untuk sebagian besar kode aplikasi, biaya itu bisa diabaikan, tetapi perlu kamu ketahui saat membangun kode ringkas bergaya embedded yang ukuran binary-nya penting.

Catatan tentang Batasan Tipe

Beberapa sistem generic membolehkan kamu membatasi parameter tipe ("T harus mendukung ==", "T harus mengimplementasikan Iterator"). Dukungan batasan tipe di Zero pra-1.0 masih terus berkembang. Contoh-contoh di repo resmi memakai generic dalam bentuk polosnya, tanpa batasan yang rumit. Saat bahasanya makin matang, sintaks batasan tipe kemungkinan akan hadir dalam bentuk yang kecil dan teratur, konsisten dengan bagian lain bahasa ini. Untuk sekarang, tulis generic yang bisa bekerja untuk setiap T yang benar-benar kamu kirim, dan biarkan compiler memberitahumu saat sebuah operasi tidak didukung.

Berikutnya: Enum

Generic membuatmu bisa memberi parameter pada tipe. Bahan dasar berikutnya ada di ujung yang berlawanan: enum, tipe enumerasi sederhana di Zero untuk kasus saat variannya tidak membawa data tambahan.

Pertanyaan yang sering diajukan

Bagaimana cara kerja generic di Zero?

Deklarasikan parameter tipe di dalam kurung sudut setelah nama fungsi atau shape: fun makePair<T, U>(left: T, right: U) -> Pair<T, U> atau shape Pair<T, U> { left: T, right: U }. Pemanggil bisa menetapkan parameternya secara eksplisit (Pair<i32, u8>) atau membiarkan compiler menginferensinya dari argumen pemanggilan.

Apakah shape bisa generic di Zero?

Bisa. Shape bisa menerima parameter tipe dengan sintaks kurung sudut yang sama seperti pada fungsi: shape Pair<T, U> { left: T, right: U }. Setiap field bisa memakai parameter itu di tipenya. Instance dibuat dengan menulis tipe yang sudah diberi parameter, misalnya Pair<i32, u8>.

Apakah parameter tipe harus ditulis saat memanggil fungsi generic?

Biasanya tidak. Compiler menginferensinya dari tipe argumen. Memanggil makePair(40, 2_u8) sudah cukup: T menjadi i32 dan U menjadi u8. Kamu bisa menetapkan parameternya secara eksplisit saat inferensi akan memilih tipe yang salah atau saat kamu ingin tempat pemanggilannya ikut mendokumentasikan tipe tersebut.

Apa itu alias tipe di Zero?

Alias tipe adalah nama singkat untuk ekspresi tipe yang lebih panjang. type BytePair = Pair<u8, u8> membuatmu bisa menulis BytePair di mana pun kamu biasanya menulis Pair<u8, u8>. Alias murni fitur penamaan: alias tidak membuat tipe baru, hanya cara yang lebih singkat untuk merujuk ke tipe yang sudah ada.

Di mana saja generic muncul di standard library Zero?

Di mana-mana, yaitu di setiap tempat sebuah tipe perlu menyimpan atau mengolah elemen bertipe apa saja. Maybe<T> untuk nilai opsional, Span<u8> untuk potongan byte, dan tipe container yang diberi parameter tipe elemennya. Mekanisme generic yang sama menangani tipe buatan pengguna maupun tipe standard library.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai