Konvensi argumen Mojo pada era pra-1.0 ditulis dengan keyword inout dan owned, ditambah borrowed untuk peminjaman. Ketiganya dihapus sejak rilis 1.0 dan digantikan konvensi tanpa keyword, mut, dan var. Topik ini menggarap dua konvensi terkini yang menulis dan mengonsumsi nilai secara mendalam, lalu memetakan keyword lama ke padanannya agar materi lama tetap terbaca dengan benar. Pemetaan ini juga berguna saat signature pustaka standar dibaca, karena kedua konvensi terkini tersebut muncul pada banyak tipe dan metode bawaan.
Peta keyword lama ke konvensi terkini#
Nama keyword lama masih sering muncul pada artikel, contoh kode, dan diskusi yang ditulis sebelum 1.0, sehingga pembacaan materi lama membutuhkan peta penerjemahan. Tabel berikut memuat status ketiganya pada Mojo 1.2.0 beserta padanannya yang resmi.
| Keyword lama | Status di 1.2.0 | Padanan kini |
|---|---|---|
borrowed | dihapus | parameter tanpa keyword, yaitu peminjaman immutable |
inout | dihapus | mut |
owned | dihapus | var, dengan transfer ^ pada pemanggilan |
Jalankan berkas berikut yang masih menuliskan inout untuk melihat bentuk penolakannya.
def ganti(inout a: Int):
a = 99
def main():
var x = 5
ganti(x)
print(x)# jalankan berkas yang masih memakai keyword inout dari era lama
mojo run kata-lama.mojo 2>&1 | sed -e "s|$PWD/||" -e "s|$HOME/|~/|"kata-lama.mojo:1:17: error: expected ')' in argument list
def ganti(inout a: Int):
^
~/.pixi/envs/mojo/bin/mojo: error: failed to parse the provided Mojo source moduleKetiga keyword lama gagal dengan pesan yang sama, yaitu expected ')' in argument list, karena parser tidak lagi mengenalinya sebagai konvensi argumen. Pesannya tampak tidak berhubungan dengan konvensi, tetapi letaknya selalu pada tanda kurung signature, sehingga mudah dilacak. Terjemahkan borrowed menjadi parameter tanpa keyword, inout menjadi mut, dan owned menjadi var sebelum kode lama dijalankan pada versi terkini.
Terjemahan tiga keyword tersebut hanya menyentuh signature, sedangkan isi fungsi dan pemanggilannya umumnya tetap. Keyword borrowed boleh dihapus begitu saja karena padanannya adalah keadaan bawaan, sedangkan inout dan owned diganti satu kata dengan mut dan var. Sisa pekerjaannya adalah memeriksa tempat pemanggilan, karena argumen yang diteruskan ke parameter var wajib memakai ^ bila tipenya tidak dapat disalin.
# tidak-dijalankan
def baca(borrowed a: Int): # menjadi: def baca(a: Int):
def ganti(inout a: Int): # menjadi: def ganti(mut a: Int):
def konsumsi(owned a: Int): # menjadi: def konsumsi(var a: Int):Peminjaman yang dapat diubah lewat mut#
Konvensi mut memberi fungsi referensi yang dapat diubah ke nilai milik pemanggil. Fungsi menulis lewat jalur ini, dan perubahannya langsung terlihat oleh pemanggil tanpa memerlukan nilai balikan. Kepemilikan tidak berpindah, sehingga nilai tetap sah setelah pemanggilan selesai. Eksklusivitas peminjaman tetap berlaku di sini, karena mut adalah jalur penulisan yang tidak boleh bertumpuk dengan peminjaman lain pada nilai yang sama.
Jalankan program berikut yang menukar isi dua variabel pemanggil lewat dua parameter mut.
def tukar(mut a: Int, mut b: Int):
var sementara = a
a = b
b = sementara
def main():
var x = 1
var y = 2
tukar(x, y)
print(x, y)# jalankan program yang menukar isi dua variabel lewat mut
mojo run tukar.mojo2 1Keluaran 2 1 membuktikan penulisan lewat a dan b terjadi pada x dan y milik pemanggil, bukan pada salinannya. Sebelum ditukar, nilai a dibaca satu kali untuk mengisi sementara, dan pembacaan semacam ini sah pada parameter mut karena peminjaman yang dapat diubah tetap boleh membaca. Pola ini menggantikan kebiasaan mengembalikan nilai balikan ganda dari fungsi, karena perubahan langsung terjadi pada tempat penyimpanan pemanggil. Pada metode struct, mekanismenya sama persis, yaitu mutasi self membutuhkan parameter mut self sebagaimana tertulis pada signature metode mutator.
Transfer yang mengonsumsi lewat var#
Konvensi var menjadikan parameter pemilik sebuah nilai yang baru. Pemanggil harus melepaskan nilainya, dan untuk tipe yang tidak dapat disalin, pelepasan itu dituliskan dengan operator ^ pada argumen. Di dalam fungsi, parameter var berlaku seperti variabel lokal yang diwarisi, sehingga boleh dibaca, diubah, atau dipindahkan lagi ke tempat lain. Untuk tipe yang dapat disalin seperti Int, pemanggilan tanpa ^ tetap sah karena compiler membuat salinannya. Sesudah transfer berjalan, variabel sumber tidak terinisialisasi, sehingga pemakaiannya ditolak compiler sebagaimana perilaku operator transfer pada umumnya.
Jalankan program berikut yang memakai kedua konvensi tersebut pada struct yang tidak dapat disalin.
struct Catatan:
var isi: String
def __init__(out self, isi: String):
self.isi = isi
def panjangkan(mut c: Catatan, tambahan: String):
c.isi += tambahan
def konsumsi(var c: Catatan):
print("dikonsumsi:", c.isi)
def main():
var c = Catatan("awal")
panjangkan(c, "-lanjutan")
print(c.isi)
konsumsi(c^)
print("selesai")# jalankan program yang memakai konvensi mut dan var
mojo run mut-var.mojoawal-lanjutan
dikonsumsi: awal-lanjutan
selesaiFungsi panjangkan menerima mut dan menambah isi Catatan tanpa memindahkan kepemilikannya, sehingga print setelahnya masih membaca isi terbaru dari variabel c. Fungsi konsumsi menerima var dan dipanggil dengan c^, yang menandakan kepemilikan nilai Catatan diserahkan kepada parameter tersebut. Baris print("selesai") berjalan tanpa masalah karena tidak lagi menyentuh c sesudah transfer. Apabila pemanggilan dilakukan tanpa ^, compiler menolaknya, dan bentuk penolakannya ditunjukkan pada bagian Kesalahan umum. Pemilihan di antara kedua konvensi ini pun mengikuti maksud, yaitu mut dipilih bila pemanggil masih membutuhkan nilainya, sedangkan var dipilih bila fungsi memang menjadikan nilai itu miliknya.
Kesalahan umum#
- Menulis keyword lama pada signature.
inout,owned, danborrowedtidak dikenal parser Mojo 1.2.0, dan pesannya berupa error sintaks pada tanda kurung daftar argumen. Perbaikannya adalah menerjemahkan keyword tersebut sesuai pemetaan pada tabel di atas. - Meneruskan nilai yang tidak dapat disalin ke parameter
vartanpa^. Parametervarmembutuhkan kepemilikan nilai, sedangkan tanpa^compiler harus menyalin, dan salinan implisit itulah yang ditolak. Jalankan berkas berikut untuk melihat pesannya.
struct Catatan:
var isi: String
def __init__(out self, isi: String):
self.isi = isi
def konsumsi(var c: Catatan):
print("dikonsumsi:", c.isi)
def main():
var c = Catatan("awal")
konsumsi(c)
print(c.isi)# jalankan berkas yang meneruskan nilai ke parameter var tanpa transfer
mojo run tanpa-transfer.mojo 2>&1 | sed -e "s|$PWD/||" -e "s|$HOME/|~/|"tanpa-transfer.mojo:12:14: error: value of type 'Catatan' cannot be implicitly copied, it does not conform to 'ImplicitlyCopyable'
konsumsi(c)
^
tanpa-transfer.mojo:12:14: note: consider transferring the value with '^'
konsumsi(c)
^
^
~/.pixi/envs/mojo/bin/mojo: error: failed to parse the provided Mojo source modulePerbaikannya mengikuti maksud pemanggilannya. Tuliskan konsumsi(c^) bila kepemilikan memang berpindah ke fungsi, dan jangan menyentuh c sesudahnya. Apabila pemanggil masih membutuhkan nilainya, sediakan salinan eksplisit pada tipe yang mendukungnya, atau ubah signature menjadi mut bila yang diinginkan hanyalah perubahan tanpa perpindahan kepemilikan.
Ringkasan#
- Keyword lama
borrowed,inout, danowneddihapus sejak 1.0, dan padanannya kini adalah parameter tanpa keyword,mut, sertavardengan transfer^. - Konvensi
mutmenulis ke nilai pemanggil melalui referensi yang dapat diubah, tanpa memindahkan kepemilikan dan tanpa menyalin. - Konvensi
varmenjadikan parameter pemilik nilai baru, sehingga pemanggil menyerahkan nilainya dengan^untuk tipe yang tidak dapat disalin.
Referensi#
- Intro to value ownership — Mojo Manual — Mendokumentasikan konvensi argumen terkini beserta transfer kepemilikannya.
- Mojo Manual — Rujukan umum bahasa Mojo beserta aturan nilai dan memori.