Penanganan Error

Pelaksanaan terlindungi dengan pcall dan xpcall beserta bentuk objek error di Lua.

Daftar isi

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.

lua pcall-dasar.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+: (.+)$"))
text Keluaran
false
attempt to perform arithmetic on a nil value

Status 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.

Alur pcallpanggilan terlindungierrordiangkat?tidaktruenilai balik fungsiyafalseobjek error
Alur pcall, dari panggilan terlindungi hingga nilai balik

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.

lua error-level.lua
-- 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))
text Keluaran
true	5.0
false	pembagi tidak boleh nol

Pemanggilan 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.

lua error-table.lua
-- 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"))
text Keluaran
false
E_FORMAT
teks "tujuh" bukan angka yang sah
true	72

Pada 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.

lua assert-dasar.lua
-- 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+: (.+)$"))
text Keluaran
sensorA	31
false
data sensorX tidak ditemukan

Panggilan 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.

lua xpcall-dasar.lua
-- 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)
text Keluaran
false
attempt to perform arithmetic on a nil value

Di 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#

  • pcall menjalankan fungsi secara terlindungi dan mengembalikan false beserta objek error apabila error diangkat, atau true beserta nilai balik apabila normal.
  • Level pada error menentukan 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.
  • assert mengangkat error bila nilainya false atau nil, sedangkan xpcall menjalankan fungsi penangan sebelum stack terurai.

Referensi#