Konvensi argumen menentukan bagaimana nilai dari pemanggil diteruskan ke parameter fungsi, dan Mojo menetapkannya secara eksplisit pada signature setiap fungsi. Topik ini membahas tiga konvensi terkini, yaitu parameter tanpa kata kunci untuk peminjaman (borrowing), kata kunci mut untuk referensi yang dapat diubah, dan var untuk perpindahan kepemilikan (ownership), lengkap dengan implikasinya terhadap nilai di luar fungsi. Kata kunci lama borrowed, inout, dan owned yang dihapus sejak rilis 1.0 dipetakan secara singkat ke penggantinya. Setiap perilaku diperagakan pada berkas yang dapat dijalankan, termasuk penulisan yang ditolak compiler beserta pesan error aslinya, sehingga pemilihan konvensi dapat dilakukan dengan sadar pada setiap fungsi.
Konvensi bawaan: peminjaman yang tak dapat diubah#
Parameter tanpa kata kunci apa pun meminjam nilai milik pemanggil melalui referensi yang tak dapat diubah. Fungsi dapat membaca nilai tersebut selancar membaca variabel biasa, tetapi tidak dapat mengubahnya dan tidak menyalinnya secara diam-diam. Konvensi ini menjadi pilihan bawaan karena mayoritas argumen hanya dibaca, sehingga nilai berukuran besar tidak pernah disalin tanpa perintah eksplisit.
Berkas baca.mojo menjumlahkan seluruh elemen sebuah List[Int] melalui argumen tanpa kata kunci. Perulangan for nilai in daftar membaca elemen satu per satu, dan panjang daftar diperiksa kembali di luar fungsi untuk menunjukkan bahwa daftar aslinya tidak tersentuh.
def jumlah(daftar: List[Int]) -> Int:
var total = 0
for nilai in daftar:
total += nilai
return total
def main():
var nilai: List[Int] = [10, 20, 30]
print(jumlah(nilai))
print(len(nilai))# argumen tanpa kata kunci meminjam tanpa menyalin
mojo run baca.mojo60
3Keluaran 60 adalah jumlah dari ketiga elemen, sedangkan 3 menunjukkan bahwa panjang daftar tetap setelah pemanggilan. Nilai yang dipinjam tidak berpindah dan tidak berubah, sehingga pemanggil dapat melanjutkan penggunaannya tanpa risiko. Penugasan pada argumen bawaan di dalam fungsi ditolak compiler, sebagaimana diperagakan pada bagian Kesalahan umum.
Argumen mut: referensi yang dapat diubah#
Kata kunci mut pada argumen menyatakan referensi yang dapat diubah, sehingga penugasan di dalam fungsi menyasar penyimpanan milik pemanggil. Perilaku ini berbeda dari Python. Pada Python, penugasan pada nama parameter hanya mengaitkan ulang nama lokal tanpa menyentuh nilai pemanggil. Pada Mojo, fungsi dengan argumen mut mengubah nilai aslinya, dan perubahannya terbaca di luar fungsi.
Berkas mutasi.mojo memuat dua fungsi dengan argumen mut. Fungsi ganti menugaskan 99 pada argumen mut a: Int, sedangkan fungsi tambah_item menambahkan elemen pada List[Int] lewat argumen mut daftar.
def ganti(mut a: Int):
a = 99
def tambah_item(mut daftar: List[Int]):
daftar.append(4)
def main():
var x = 5
ganti(x)
print(x)
var daftar: List[Int] = [1, 2, 3]
tambah_item(daftar)
print(daftar)# perubahan lewat argumen mut terlihat di luar fungsi
mojo run mutasi.mojo99
[1, 2, 3, 4]Keluaran 99 menunjukkan bahwa penugasan pada argumen mut mengubah variabel x milik pemanggil, dan [1, 2, 3, 4] menunjukkan elemen yang ditambahkan tetap ada setelah fungsi berakhir. Pembaca signature langsung mengetahui fungsi mana yang menulis nilai pemanggilnya, karena penanda mut wajib ditulis pada setiap argumen yang hendak diubah.
Konvensi mut menuntut eksklusivitas, yaitu sebuah nilai tidak boleh dipinjam secara dapat diubah dan tak dapat diubah pada saat bersamaan. Berkas alias.mojo melanggar aturan tersebut dengan mengirim variabel pesan yang sama sebagai argumen mut s sekaligus argumen bawaan other dalam satu pemanggilan.
def gabung(mut s: String, other: String):
s += other
s += other
def main():
var pesan = "hai"
gabung(pesan, pesan)
print(pesan)# peminjaman mut dan bawaan pada nilai yang sama ditolak
mojo run alias.mojo/home/kal/my-project/content/mojo/dua-jenis-fungsi/konvensi-argumen/alias.mojo:8:11: error: aliasing values passed mutably to 's' argument and passed immutably to 'other' argument in 'gabung' call
gabung(pesan, pesan)
^~~~~~ ~~~~~
/home/kal/my-project/content/mojo/dua-jenis-fungsi/konvensi-argumen/alias.mojo:8:11: note: 'origin_of(pesan)' value is passed through aliasing 'imm' argument 'other'
gabung(pesan, pesan)
^ ~~~~~
/home/kal/.pixi/envs/mojo/bin/mojo: error: failed to parse the provided Mojo source modulePesan error menyebut argumen yang bertabrakan, yaitu s yang diteruskan secara dapat diubah dan other yang dipinjam secara tak dapat diubah, keduanya berasal dari nilai yang sama. Eksklusivitas mencegah fungsi membaca nilai pada saat nilai yang sama sedang ditulis, sehingga hasil setiap pembacaan selalu konsisten. Perbaikannya adalah mengirim nilai yang berbeda atau mengubah konvensi salah satu argumennya.
Argumen var: kepemilikan berpindah#
Kata kunci var pada argumen menyatakan bahwa fungsi menjadi pemilik nilai tersebut, sehingga nilai berpindah dari pemanggil ke fungsi. Untuk tipe yang dapat disalin seperti Int, perpindahan berbentuk salinan, sehingga perubahan di dalam fungsi hanya menyentuh salinannya. Untuk tipe yang tidak mendukung penyalinan implisit seperti List, kepemilikan memang berpindah, dan pemanggil menyerahkannya dengan operator transfer ^.
Berkas salin.mojo menugaskan 77 pada argumen var a: Int kemudian mencetak nilai tersebut di dalam dan di luar fungsi. Nilai x milik pemanggil turut dicetak sebagai pembanding.
def salin(var a: Int):
a = 77
print("di dalam fungsi:", a)
def main():
var x = 5
salin(x)
print("di luar fungsi:", x)# argumen var pada tipe yang dapat disalin
mojo run salin.mojodi dalam fungsi: 77
di luar fungsi: 5Keluaran menunjukkan bahwa kedua nilai berbeda karena penugasan di dalam fungsi hanya mengubah salinan yang dimiliki fungsi, sehingga x milik pemanggil tetap 5. Pada tipe lain yang tidak dapat disalin, pola yang sama tidak dapat berjalan tanpa penyerahan kepemilikan. Berkas salin-daftar.mojo mencoba mengirim List[Int] ke argumen var tanpa operator transfer, dan compiler menolaknya.
def konsumsi(var daftar: List[Int]):
daftar.append(4)
def main():
var nilai: List[Int] = [1, 2, 3]
konsumsi(nilai)# List tidak dapat disalin implisit untuk argumen var
mojo run salin-daftar.mojo/home/kal/my-project/content/mojo/dua-jenis-fungsi/konvensi-argumen/salin-daftar.mojo:7:14: error: value of type 'List[Int]' cannot be implicitly copied, it does not conform to 'ImplicitlyCopyable'
konsumsi(nilai)
^~~~~
/home/kal/my-project/content/mojo/dua-jenis-fungsi/konvensi-argumen/salin-daftar.mojo:7:14: note: consider transferring the value with '^'
konsumsi(nilai)
^
^
/home/kal/my-project/content/mojo/dua-jenis-fungsi/konvensi-argumen/salin-daftar.mojo:7:14: note: you can copy it explicitly with '.copy()'
konsumsi(nilai)
^
.copy()
/home/kal/.pixi/envs/mojo/bin/mojo: error: failed to parse the provided Mojo source modulePesan error menyatakan bahwa nilai List[Int] tidak dapat disalin secara implisit karena tidak memenuhi trait ImplicitlyCopyable, yaitu kontrak bahasa untuk tipe yang boleh disalin tanpa perintah eksplisit. Compiler menawarkan dua jalan, yaitu transfer dengan ^ atau salinan eksplisit dengan .copy(). Berkas transfer.mojo menerapkan saran pertama.
def konsumsi(var daftar: List[Int]):
daftar.append(4)
print("di dalam fungsi:", daftar)
def main():
var nilai: List[Int] = [1, 2, 3]
konsumsi(nilai^)# kepemilikan diserahkan dengan operator transfer
mojo run transfer.mojodi dalam fungsi: [1, 2, 3, 4]Keluaran menunjukkan bahwa fungsi menerima kepemilikan daftar lalu menambahkan elemen 4. Tanda ^ pada pemanggilan menyatakan kepemilikan nilai diserahkan penuh, sehingga fungsi bebas mengubah atau mengonsumsinya tanpa menyalin konten daftar. Konsekuensinya diuraikan pada bagian Kesalahan umum.
Kesalahan umum#
Kesalahan pertama adalah menugaskan nilai pada argumen tanpa kata kunci. Argumen bawaan hanyalah peminjaman yang tak dapat diubah, sehingga penugasan kepadanya ditolak compiler. Berkas ubah-baca.mojo memuat fungsi ubah yang mencoba menugaskan 99 pada argumen bawaannya.
def ubah(a: Int):
a = 99
def main():
var x = 5
ubah(x)
print(x)# penugasan pada argumen bawaan ditolak compiler
mojo run ubah-baca.mojo/home/kal/my-project/content/mojo/dua-jenis-fungsi/konvensi-argumen/ubah-baca.mojo:2:5: error: expression must be mutable in assignment
a = 99
^
/home/kal/.pixi/envs/mojo/bin/mojo: error: failed to parse the provided Mojo source modulePesan expression must be mutable in assignment menyatakan bahwa a bukan penyimpanan milik fungsi, melainkan peminjaman yang tak dapat diubah. Apabila fungsi memang perlu mengubah nilai pemanggil, deklarasikan argumennya dengan mut, dan apabila perubahan hanya perlu berlaku di dalam fungsi, terima nilainya dengan argumen var. Aturan ini menjadikan maksud setiap fungsi terbaca langsung dari signature-nya.
Kesalahan kedua adalah melanjutkan penggunaan variabel yang kepemilikannya sudah ditransfer. Berkas pakai-transfer.mojo mengirim nilai dengan ^ lalu mencetaknya sesudah pemanggilan.
def konsumsi(var daftar: List[Int]):
daftar.append(4)
def main():
var nilai: List[Int] = [1, 2, 3]
konsumsi(nilai^)
print(nilai)# variabel setelah transfer tidak lagi terinisialisasi
mojo run pakai-transfer.mojo/home/kal/my-project/content/mojo/dua-jenis-fungsi/konvensi-argumen/pakai-transfer.mojo:8:10: error: use of uninitialized value 'nilai'
print(nilai)
^
/home/kal/my-project/content/mojo/dua-jenis-fungsi/konvensi-argumen/pakai-transfer.mojo:6:9: note: 'nilai' declared here
var nilai: List[Int] = [1, 2, 3]
^
/home/kal/.pixi/envs/mojo/bin/mojo: error: failed to run the pass managerPesan error use of uninitialized value 'nilai' menyatakan bahwa variabel tersebut tidak lagi terinisialisasi setelah transfer, dan compiler melacak asal deklarasinya pada pesan berikutnya. Setelah kepemilikan diserahkan, pemanggil tidak memiliki nilai apa pun untuk dibaca. Perbaikannya adalah tidak menyentuh variabel tersebut lagi, mengubah argumen menjadi mut apabila perubahan memang diinginkan, atau mengirim salinan eksplisit dengan nilai.copy() apabila nilai aslinya perlu dipertahankan.
Ringkasan#
- Argumen tanpa kata kunci meminjam nilai secara tak dapat diubah, sehingga fungsi dapat membaca tetapi tidak dapat menulis dan tidak menyalinnya.
- Argumen
mutadalah referensi yang dapat diubah, perubahannya terlihat di luar fungsi, dan pemakaiannya menuntut eksklusivitas terhadap peminjaman lain pada nilai yang sama. - Argumen
varmemindahkan kepemilikan, berbentuk salinan pada tipe yang dapat disalin dan transfer^pada tipe sepertiList. - Variabel yang kepemilikannya ditransfer tidak boleh dibaca lagi, sedangkan pemetaan kata kunci lama adalah
borrowedke bawaan,inoutkemut, danownedkevar.
Referensi#
- Functions — Mojo Manual — Deklarasi fungsi dan konvensi argumen terkini pada Mojo 1.2.0.
- Mojo Manual — Modular — Dokumentasi resmi bahasa Mojo sebagai rujukan aturan peminjaman dan kepemilikan.
- Mojo releases — Modular — Lini masa rilis resmi yang mencatat penghapusan kata kunci konvensi argumen lama sejak 1.0.