Konstanta dan variabel adalah wadah nilai dalam program, dan Odin menyediakan bentuk deklarasi yang khas untuk keduanya. Topik ini membahas deklarasi konstanta dengan ::, deklarasi variabel melalui inferensi tipe (type inference) dengan :=, deklarasi dengan tipe eksplisit, nilai nol (zero value) dari variabel tanpa nilai awal, deklarasi berganda dalam satu pernyataan, serta shadowing dalam blok bersarang. Setiap bentuk dibuktikan dengan program yang benar-benar dijalankan, sehingga perilakunya dapat dibaca langsung dari keluaran aslinya. Setelah topik ini dibaca, bentuk deklarasi yang tepat dapat dipilih untuk setiap kebutuhan, dan pesan error yang muncul saat bentuknya keliru dapat ditafsirkan dengan benar.
Konstanta dan konstanta tak bertipe#
Konstanta dideklarasikan dengan ::, dan nilainya ditetapkan sekali untuk selamanya. Nilai konstanta harus dapat dihitung pada waktu kompilasi, sehingga ekspresi seperti 9.0 / 5.0 sah dijadikan konstanta. Percobaan menetapkan nilai baru ke konstanta ditolak compiler dengan pesan Error: Cannot assign to 'NAMA'. Nama konstanta pada contoh ditulis dengan huruf kapital pada setiap kata, misalnya TITIK_BEKU_C, agar mudah dibedakan dari variabel.
Konstanta yang dideklarasikan tanpa anotasi tipe bersifat tak bertipe (untyped constant). Konstanta tak bertipe tidak terikat pada satu tipe, melainkan menyesuaikan diri secara implisit dengan tipe konteks yang memintanya, selama tipe tersebut dapat menampung nilainya. Karena itu, satu konstanta dapat dipakai di banyak tempat dengan tipe berbeda tanpa konversi eksplisit. Sifat ini dibuktikan pada program berikut. Konstanta TITIK_BEKU_C :: 0 ditampung oleh variabel int sekaligus variabel f32, lalu ukuran penampungnya diukur dengan size_of, karena int berukuran 8 byte pada target 64-bit sedangkan f32 berukuran 4 byte. Konstanta CELSIUS_TO_FAHRENHEIT :: 9.0 / 5.0 kemudian dipakai dalam perhitungan f64 dan f32 yang keluarannya dibandingkan.
package main
import "core:fmt"
// Konstanta tak bertipe: satu nilai, dapat dipakai lintas tipe
TITIK_BEKU_C :: 0
CELSIUS_TO_FAHRENHEIT :: 9.0 / 5.0
main :: proc() {
// satu konstanta dipakai sebagai int dan f32 sekaligus
beku_int := TITIK_BEKU_C
beku_f32: f32 = TITIK_BEKU_C
fmt.printf("titik beku %d sebagai int, ukurannya %d byte\n", beku_int, size_of(beku_int))
fmt.printf("titik beku %v sebagai f32, ukurannya %d byte\n", beku_f32, size_of(beku_f32))
// satu konstanta konversi dipakai dalam perhitungan f64 dan f32
suhu := 21
hasil_f64 := f64(suhu) * CELSIUS_TO_FAHRENHEIT + 32.0
hasil_f32: f32 = f32(suhu) * CELSIUS_TO_FAHRENHEIT + 32
fmt.printf("f64: %.9f\n", hasil_f64)
fmt.printf("f32: %.9f\n", hasil_f32)
}odin run konstanta.odin -filetitik beku 0 sebagai int, ukurannya 8 byte
titik beku 0 sebagai f32, ukurannya 4 byte
f64: 69.800000000
f32: 69.800003052Keluaran menegaskan bahwa kedua variabel menampung konstanta yang sama tetapi berukuran 8 byte dan 4 byte, sehingga keduanya benar-benar bertipe berbeda. Selisih presisi pada baris f32 muncul karena 1.8 tidak dapat direpresentasikan secara persis dalam biner, dan pembulatan yang dihasilkan berbeda antara f32 dan f64. Sifat lintas tipe itu hilang apabila konstanta diberi tipe secara eksplisit, misalnya BATAS: int : 100. Konstanta bertipe berperilaku seperti nilai int biasa, sehingga penetapannya ke variabel f32 ditolak dengan pesan ketidakcocokan tipe.
Deklarasi variabel dengan inferensi dan tipe eksplisit#
Variabel dideklarasikan dalam dua bentuk utama. Bentuk suhu_c := 30 mendeklarasikan variabel, menetapkan nilai awalnya, dan menyimpulkan tipenya dari nilai tersebut, yaitu int. Bentuk batas_atas: int = 100 menuliskan tipe secara eksplisit sebelum nilai awal. Kedua bentuk menghasilkan variabel yang perilakunya sama setelah deklarasi, sehingga pilihannya adalah perkara kejelasan maksud. Tipe eksplisit umumnya dipakai ketika hasil perhitungan wajib bertipe tertentu, seperti suhu_f: f64 = f64(suhu_c) * CELSIUS_TO_FAHRENHEIT + 32.0 yang menyatakan bahwa hasil konversi suhu ditampung dalam f64.
Penetapan nilai baru ke variabel yang sudah ada ditulis dengan = tanpa titik dua, seperti suhu_c = 21. Tanda := khusus untuk deklarasi, sedangkan tanda = khusus untuk penetapan. Penggunaan = pada nama yang belum dideklarasikan menghasilkan Error: Undeclared name, sedangkan deklarasi := yang diulang untuk nama yang sama menghasilkan Error: Redeclaration of 'x' in this scope. Kedua pesan tersebut dibahas lebih rinci pada bagian Kesalahan umum. Program utuh berikut memuat seluruh bentuk deklarasi yang dibahas pada topik ini, mulai dari inferensi tipe hingga blok bersarang.
package main
import "core:fmt"
import "core:strings"
// Konstanta konversi suhu untuk perhitungan Fahrenheit
CELSIUS_TO_FAHRENHEIT :: 9.0 / 5.0
main :: proc() {
// deklarasi dengan inferensi tipe melalui :=
suhu_c := 30
fmt.printfln("Suhu awal: %d derajat Celsius", suhu_c)
// penetapan ke variabel yang sudah ada
suhu_c = 21
fmt.printfln("Suhu terkini: %d derajat Celsius", suhu_c)
// deklarasi dengan tipe eksplisit
batas_atas: int = 100
fmt.printfln("Batas atas pencatatan: %d derajat", batas_atas)
// konversi suhu ke Fahrenheit dengan konstanta konversi
suhu_f: f64 = f64(suhu_c) * CELSIUS_TO_FAHRENHEIT + 32.0
fmt.printf("%.1f derajat Celsius = %.1f derajat Fahrenheit\n", f64(suhu_c), suhu_f)
// deklarasi tanpa nilai awal: setiap tipe mendapat nilai nolnya
total: int
catatan: string
terverifikasi: bool
penunjuk: ^int
fmt.printf("total = %d, catatan = %q, terverifikasi = %t, penunjuk = %v\n",
total, catatan, terverifikasi, penunjuk)
// penghitung kata: nilai nol int menjadi titik awal penghitungan
teks := "odin adalah bahasa pemrograman yang berorientasi data"
kata := strings.fields(teks)
for kata_i in kata {
total += 1
fmt.printf("%d: %s\n", total, kata_i)
}
fmt.printfln("Jumlah kata: %d", total)
// deklarasi berganda dan dua deklarasi dalam satu baris
dingin, panas := 10, 30
hujan := false; berkabut := true
fmt.printfln("dingin di bawah %d, panas di atas %d", dingin, panas)
fmt.printfln("hujan = %t, berkabut = %t", hujan, berkabut)
// blok bersarang: deklarasi ulang nama yang sama (shadowing)
{
suhu_c := 100
fmt.printfln("Di dalam blok: %d", suhu_c)
}
fmt.printfln("Di luar blok: %d", suhu_c)
}odin run main.odin -fileSuhu awal: 30 derajat Celsius
Suhu terkini: 21 derajat Celsius
Batas atas pencatatan: 100 derajat
21.0 derajat Celsius = 69.8 derajat Fahrenheit
total = 0, catatan = "", terverifikasi = false, penunjuk = 0x0
1: odin
2: adalah
3: bahasa
4: pemrograman
5: yang
6: berorientasi
7: data
Jumlah kata: 7
dingin di bawah 10, panas di atas 30
hujan = false, berkabut = true
Di dalam blok: 100
Di luar blok: 21Nilai nol dan penghitung kata#
Variabel yang dideklarasikan tanpa nilai awal tidak berisi sampah memori, melainkan selalu diisi nilai nol tipenya. Nilai nol untuk tipe numerik adalah 0, untuk bool adalah false, untuk string adalah string kosong "", dan untuk pointer adalah nil. Bentuk total: int cukup ditulis dengan nama, titik dua, dan tipe, tanpa tanda sama dengan, karena compiler mengisi nilai nol secara otomatis. Pada keluaran program di atas, deklarasi catatan: string tercetak sebagai "", variabel terverifikasi tercetak sebagai false, dan pointer penunjuk tercetak sebagai 0x0, yaitu tampilan alamat untuk nil.
Nilai nol bukan sekadar kebiasaan penulisan, melainkan titik awal yang langsung dapat dipakai. Pada program tersebut, total: int dideklarasikan sebagai penghitung kata, lalu nilainya naik satu untuk setiap kata yang dikembalikan strings.fields(teks). Proc strings.fields dari package core:strings memecah string menjadi kata-katanya dan mengembalikan []string, sehingga perulangan for kata_i in kata mengunjungi setiap kata secara berurutan. Keluaran menunjukkan penghitung bergerak dari nilai nol hingga 7, tanpa satu baris pengisian nilai awal pun.
Deklarasi berganda dan shadowing#
Beberapa deklarasi dapat ditulis dalam satu pernyataan maupun satu baris. Bentuk dingin, panas := 10, 30 mendeklarasikan dua variabel dalam satu pernyataan, dengan tipe masing-masing disimpulkan dari nilainya. Bentuk hujan := false; berkabut := true menempatkan dua pernyataan dalam satu baris dengan pemisah titik koma, karena titik koma pada Odin berperan sebagai pemisah pernyataan. Bentuk satu baris ini sah, tetapi satu deklarasi per baris tetap menjadi bentuk yang paling mudah dibaca.
Di dalam blok bersarang, nama yang sudah dipakai boleh dideklarasikan ulang, dan perilaku ini disebut shadowing. Blok { suhu_c := 100 ... } mendeklarasikan suhu_c baru yang hanya hidup di dalam bloknya, sedangkan suhu_c milik proc tetap utuh dan nilainya kembali terbaca setelah blok berakhir, sebagaimana keluaran menampilkan 100 diikuti 21. Shadowing berbeda dari deklarasi ulang pada lingkup (scope) yang sama, karena blok dalam adalah lingkup baru yang sah untuk nama apa pun. Apabila nama yang sama justru mempersulit pembacaan, pilihlah nama yang berbeda, karena shadowing yang tidak disengaja sulit dilacak.
Kesalahan umum#
Kesalahan pertama adalah menulis x = 5 pada nama yang belum dideklarasikan. Tanda = hanya menetapkan nilai ke nama yang sudah ada, sehingga compiler menolaknya dengan Error: Undeclared name: x. Perbaikannya adalah menulis x := 5 apabila nama tersebut baru pertama kali dideklarasikan.
Kesalahan kedua adalah mengulang deklarasi untuk nama yang sama dalam lingkup yang sama, misalnya x := 10 diikuti x := 20. Deklarasi wajib unik dalam lingkupnya, sehingga compiler menampilkan Error: Redeclaration of 'x' in this scope. Mengubah nilai cukup dilakukan dengan x = 20, sedangkan deklarasi ulang hanya sah di dalam blok bersarang seperti pada bagian sebelumnya.
Kesalahan ketiga adalah menulis kata kunci var sebagaimana kebiasaan pada bahasa lain, misalnya var x: int = 10. Odin tidak memiliki kata kunci var, sehingga baris tersebut gagal dengan Syntax Error: Expected ';', got identifier. Bentuk yang benar adalah x: int untuk nilai nol atau x: int = 10 untuk nilai awal tertentu.
Kesalahan keempat adalah menetapkan nilai int ke variabel bertipe f64 tanpa konversi, misalnya suhu_f: f64 = suhu dengan suhu bertipe int. Odin tidak melakukan konversi numerik secara implisit, sehingga compiler menampilkan Error: Cannot assign value 'suhu' of type 'int' to 'f64' in a variable declaration. Konversi eksplisit f64(suhu) wajib ditulis, sebagaimana dipakai pada program di atas.
Ringkasan#
- Konstanta dideklarasikan dengan
::, nilainya dihitung pada waktu kompilasi, dan konstanta tak bertipe menyesuaikan diri dengan tipe konteks yang memintanya. - Variabel dideklarasikan dengan
:=untuk inferensi tipe, dengantipe = nilaiuntuk tipe eksplisit, dan dengannama: tipetanpa nilai agar mendapat nilai nol. - Nilai nol yang selalu terisi adalah
0untuk tipe numerik,falseuntukbool, string kosong untukstring, danniluntuk pointer. - Deklarasi berganda
a, b := 10, 30, pemisah titik koma untuk satu baris, dan shadowing di blok bersarang sah dilakukan, sedangkan deklarasi ulang dalam lingkup yang sama ditolak.
Referensi#
- Odin Language Overview — odin-lang.org — Dokumentasi resmi yang mencakup deklarasi variabel, konstanta, nilai nol, dan untyped types.
- Package core:strings — pkg.odin-lang.org
— Dokumentasi proc
fieldsyang memecah string menjadi kata pada penghitung kata. - Package core:fmt — pkg.odin-lang.org — Dokumentasi pencetakan berformat yang menampilkan keluaran seluruh contoh program.