Lua mengangkat error dari dua sumber, yaitu kesalahan saat program berjalan (runtime error), misalnya operasi pada tipe yang salah, dan panggilan eksplisit terhadap fungsi error. Topik ini membahas cara menangkap error dengan pcall dan xpcall, cara menyusun pesan melalui level error, cara membawa error terstruktur dalam bentuk table, serta cara menggunakan assert. Setelah membacanya, kegagalan dapat ditangkap tanpa menghentikan seluruh skrip dan pesannya dapat disajikan dalam bentuk yang mudah diproses.
Dua sumber error dan pcall#
Setiap error di Lua dibawa sebagai sebuah nilai, yang disebut objek error, biasanya berupa string. Apabila error tidak ditangkap, pelaksanaan skrip berhenti dan pesannya ditampilkan beserta posisi berkas tempat error diangkat. Fungsi pcall, singkatan dari protected call, menjalankan sebuah fungsi secara terlindungi, yaitu error yang diangkat di dalamnya tidak menghentikan skrip. Nilai baliknya menyatakan status pelaksanaan beserta hasil atau objek errornya.
Contoh berikut sengaja mengoperasikan angka dengan nil, sebuah kesalahan tipe yang lazim terjadi. Pola local ok, err = pcall(...) pada contoh ini adalah bentuk baku penanganan error di Lua.
-- pcall-dasar.lua
-- pcall menangkap error dan mengembalikan status beserta objek errornya.
local ok, err = pcall(function()
return 1 + nil
end)
print(ok)
print(err:match(":%d+: (.+)$"))false
attempt to perform arithmetic on a nil valueStatus false menyatakan pemanggilan gagal, sedangkan err memuat objek error. Pesan error bawaan Lua menyertakan posisi berkas dan nomor baris di awalnya, sehingga contoh membuang prefiks tersebut dengan pola :%d+: (.+)$ dan hanya menampilkan pesan intinya. Apabila tidak terjadi error, pcall mengembalikan true beserta seluruh nilai balik fungsi, sehingga status dan hasil dapat diuji dalam satu pemanggilan.
Kedua jalur nilai balik tersebut dirangkum pada diagram berikut, mulai dari panggilan terlindungi hingga cabang yang menentukan hasilnya.
Level pesan pada fungsi error#
Fungsi error mengangkat objek error secara eksplisit dan menambahkan informasi posisi menurut argumen kedua yang disebut level. Level 1, yang juga menjadi nilai bawaan, menambahkan nama berkas dan nomor baris tempat error dipanggil. Informasi posisi membantu pencarian sumber kesalahan saat pengembangan, tetapi tidak selalu tepat untuk pesan yang diteruskan ke pengguna atau yang dicatat ke log, yaitu catatan jalannya program. Level 0 menghasilkan pesan apa adanya tanpa posisi, sehingga teksnya stabil dan tidak bergantung pada letak berkas.
Fungsi bagi berikut memvalidasi pembaginya dan mengangkat error dengan level 0 apabila pembaginya nol.
-- error-level.lua
-- Level 0 menghasilkan pesan error tanpa posisi berkas.
local function bagi(a, b)
if b == 0 then
error("pembagi tidak boleh nol", 0)
end
return a / b
end
print(pcall(bagi, 10, 2))
print(pcall(bagi, 10, 0))true 5.0
false pembagi tidak boleh nolPemanggilan yang berhasil mengembalikan true beserta nilai baginya, yaitu 5.0, karena operator / pada Lua 5.4 selalu menghasilkan bilangan real. Pemanggilan yang gagal mengembalikan false dan pesan pembagi tidak boleh nol tanpa prefiks posisi karena level 0. Sebagai pedoman praktis, gunakan level 1 untuk pesan yang ditujukan kepada pemrogram yang sedang mencari sumber kesalahan, dan gunakan level 0 untuk pesan yang akan diteruskan atau diproses lebih lanjut.
Objek error berbentuk table#
Objek error tidak harus berupa string. Fungsi error menerima nilai apa pun, dan table menjadi pilihan yang tepat apabila kegagalan perlu membawa lebih dari satu informasi. Bentuk table ini lazim digunakan pada aplikasi yang menyematkan Lua, karena aplikasi host, yaitu aplikasi yang menjadi tempat Lua berjalan, dapat membedakan jenis kegagalan lewat kode tanpa mengurai teks pesan.
Contoh berikut membungkus konversi teks menjadi angka. Kegagalan konversi diangkat sebagai table dengan kode dan pesan, lalu penangan membaca kedua bidang tersebut secara langsung.
-- error-table.lua
-- Objek error berbentuk table membawa kode dan pesan sekaligus.
local function konversiKeAngka(teks)
local angka = tonumber(teks)
if angka == nil then
error({
kode = "E_FORMAT",
pesan = "teks \"" .. teks .. "\" bukan angka yang sah"
}, 0)
end
return angka
end
local ok, err = pcall(konversiKeAngka, "tujuh")
print(ok)
print(err.kode)
print(err.pesan)
print(pcall(konversiKeAngka, "72"))false
E_FORMAT
teks "tujuh" bukan angka yang sah
true 72Pada kegagalan pertama, err langsung berupa table, sehingga err.kode dan err.pesan dapat diakses tanpa penguraian string. Level 0 tetap digunakan agar Lua tidak mencoba menambahkan posisi pada nilai yang bukan string. Pemanggilan kedua menunjukkan jalur normal, yaitu pcall mengembalikan true beserta hasil konversi. Menyimpan kode kegagalan sebagai konstanta seperti E_FORMAT juga memudahkan pengujian, karena penangan cukup membandingkan kode dan bukan teks pesan. Apabila jenis kegagalan bertambah, kode baru cukup ditambahkan pada table yang sama, dan cara penangan membacanya tidak perlu berubah.
assert dan xpcall#
Fungsi assert menyediakan cara singkat untuk mengangkat error bila sebuah nilai kosong. Panggilan assert(nilai, "pesan") mengembalikan nilai apabila nilainya benar, dan mengangkat error dengan "pesan" apabila nilainya false atau nil. Bentuk ini cocok untuk memvalidasi prasyarat pada awal fungsi, misalnya memastikan data yang diharapkan benar-benar ada.
-- assert-dasar.lua
-- assert mengangkat error bila nilainya false atau nil.
local dataSuhu = { sensorA = 31 }
local suhuA = dataSuhu.sensorA
assert(suhuA, "data sensorA tidak ditemukan")
print("sensorA", suhuA)
local ok, err = pcall(function()
local suhuX = dataSuhu.sensorX
assert(suhuX, "data sensorX tidak ditemukan")
end)
print(ok)
print(err:match(":%d+: (.+)$"))sensorA 31
false
data sensorX tidak ditemukanPanggilan assert yang berhasil tidak mengganggu alur program, sedangkan yang gagal mengangkat error seperti halnya error dengan level 1, sehingga contoh tetap membuang prefiks posisi sebelum mencetak. Fungsi xpcall bekerja seperti pcall, tetapi menerima fungsi penangan (handler) sebagai argumen kedua. Penangan dipanggil sebelum stack panggilan (call stack), yaitu catatan susunan pemanggilan fungsi yang sedang berjalan, terurai, sehingga penangan masih dapat membaca keadaan stack saat itu.
-- xpcall-dasar.lua
-- Handler xpcall dipanggil sebelum stack terurai.
local function tangani(err)
return err:match(":%d+: (.+)$") or err
end
local ok, hasil = xpcall(function()
return 1 + nil
end, tangani)
print(ok)
print(hasil)false
attempt to perform arithmetic on a nil valueDi dalam penangan, fungsi debug.traceback dapat dipanggil untuk merangkum seluruh stack panggilan menjadi teks pelacakan yang lazim disimpan sebagai log untuk diagnosis. Contoh di sini sengaja tidak menampilkannya dan hanya mengembalikan pesan inti, karena pelacakan penuh tergantung pada letak berkas dan memenuhi keluaran dengan informasi yang tidak stabil. Nilai balik penangan menjadi objek error yang diterima pemanggil xpcall, sehingga pembersihan pesan dapat dilakukan pada satu tempat untuk seluruh skrip.
Ringkasan#
pcallmenjalankan fungsi secara terlindungi dan mengembalikanfalsebeserta objek error apabila error diangkat, atautruebeserta nilai balik apabila normal.- Level pada
errormenentukan informasi posisi, yaitu level 1 menambahkan posisi berkas dan nomor baris, sedangkan level 0 menghasilkan pesan apa adanya. - Objek error dapat berupa table yang membawa kode dan pesan secara terstruktur agar mudah dibaca aplikasi host.
assertmengangkat error bila nilainyafalseataunil, sedangkanxpcallmenjalankan fungsi penangan sebelum stack terurai.
Referensi#
- Manual Referensi Lua 5.4 — Lua.org — Bagian 2.3 tentang penanganan error dan bagian 6.1 tentang fungsi dasar menjadi rujukan perilaku pcall, xpcall, error, dan assert.
- Programming in Lua, edisi keempat — Lua.org — Bab 8 membahas penanganan error, termasuk pesan error dan pemanggilan berlapis pcall, pada versi daringnya.