- Ante adalah desain bahasa sistem yang mencoba memakai fleksibilitas reference counting dan keamanan borrow checking secara bersamaan, sambil menghindari panic runtime ala Rust atau overhead pemeriksaan akses eksklusif ala Swift
- Mekanisme intinya adalah shape-stability dan temporary uniq conversion, yang memungkinkan pembuatan mutable borrow secara aman pada field dari nilai yang dihitung referensinya, sementara nilai di dalam union hanya diperlakukan sebagai
uniqdalam cakupan terbatas Rc<RefCell<T>>di Rust dapat menyebabkan panic saat runtime jika dipakai secara keliru, dan sistem borrowing Swift mencakup pemeriksaan akses eksklusif saat runtime, tetapi Ante mencoba menangani beberapa kasus ini dengan aturan waktu kompilasi- Ini masih merupakan desain work-in-progress yang baru sebagian diimplementasikan, dan karena perlu menganalisis tipe secara rekursif untuk menentukan apakah objek tertentu bisa dicapai, penambahan field dapat menjadi breaking API change
- Pendekatan ini melonggarkan asumsi bahwa shared mutable borrowing selalu mustahil, dan bersama teknik seperti Vale, group borrowing, dan Rust GhostCell, memperluas wilayah pengecualian dalam desain keamanan memori
Gabungan yang ingin dicapai Ante
- Ante adalah bahasa pemrograman sistem yang menargetkan Rust yang lebih sederhana dengan keamanan memori dan keamanan thread
- Model dasarnya adalah kepemilikan tunggal dan borrow checking, dengan nilai ditempatkan di stack atau secara inline di dalam struct/array yang menampungnya
- Saat ingin memprioritaskan kesederhanaan, Anda dapat memilih reference counting dengan menambahkan kata kunci
sharedpada tipe - Dengan
shared type Colordanshared type RbTree t, fungsibalancepada red-black tree menjadi sesingkat contoh Python dan lebih kecil daripada contoh C++ maupun Rust - Fokus utamanya adalah bagaimana menangani mutable borrow pada data yang dihitung referensinya tanpa risiko panic
borrow_mut()ala Rust atau pemeriksaan akses eksklusif runtime ala Swift - Ante masih dalam status work-in-progress; sebagian sudah diimplementasikan, sebagian masih teoretis, dan desainnya juga masih berubah
- Perkembangannya dapat dilihat di situs Ante dan Discord
shape-stability dan beberapa referensi mutable
- shape-stability di Ante adalah konsep bahwa “referensi ke target yang memiliki stable shape akan selalu valid meskipun perubahan apa pun terjadi di tempat lain”
- Berkat konsep ini, dimungkinkan memiliki beberapa referensi mutable borrow sekaligus terhadap struct yang sama
- Dalam contoh
heal (healer: mut Entity) (target: mut Entity), pemanggilanself_healyang meneruskanEntityyang sama ke dua argumen dimungkinkan sehingga objek dapat menyembuhkan dirinya sendiri- Walaupun
healerdantargetmenunjuk keEntityyang sama, kode ini tidak dapat menghancurkanEntitytersebut sehingga kedua referensi tetap valid
- Walaupun
- Referensi mutable ke struct itu sendiri, field-nya, dan field dari field tersebut juga dapat diizinkan secara bersamaan
- Misalnya,
ship: mut Spaceshipdanengine_alias: mut Engine = ship.enginedapat dipakai bersamaan karena dinilai bahwashipdanenginedi dalamnya tidak akan dihancurkan selama fungsi berjalan
- Misalnya,
- Rust dan Swift tidak mengizinkan beberapa referensi
&mutsekaligus menunjuk ke data yang sama
Mutable borrow pada field dari nilai yang dihitung referensinya
- Di Ante, jika
sharedditambahkan di depan definisi tipe, tipe itu akan otomatis memakai reference counting - Dalam contoh
shared mut type Spaceship,launchmemegangSpaceshipyang setara denganRcsambil meneruskanmut ship.enginekeset_fuel - Karena
launchmempertahankan objek indukSpaceship, dapat disimpulkan bahwa fieldenginedi dalamnya juga tetap hidup - Aturan umumnya adalah bahwa referensi borrow
mutselalu dapat dibuat untuk field dari tipeshared mut- Namun, ini tidak berarti mutable borrow selalu bisa dibuat untuk semua target di dalam field tersebut; aturan tambahan tetap diperlukan
- Contoh berikutnya memakai notasi
Rc Spaceshipyang lebih eksplisit alih-alih sugar syntaxshared mut type Spaceshipshared mut type Spaceshipmenjaditype Spaceship, danvar ship: Spaceshipmenjadivar ship: Rc Spaceship
Titik di mana union menimbulkan masalah keamanan
- Union menyimpan isinya secara inline sehingga mengurangi pointer chasing dan cache miss, yang menguntungkan untuk kecepatan
- Jika
union Enginedi C berada di dalamstruct Spaceship, makaStringTheoryEnginedanImpulseEngineberada langsung di memoriSpaceship - Ini kontras dengan pendekatan seperti Java yang memakai interface dan pointer
- Jika
- Masalahnya adalah union sulit didukung secara aman dalam bahasa yang aman terhadap memori
- Dalam contoh
Engineyang dapat berupaStringTheoryEngine(str: String)atauImpulseEngine(fuel: I32), segfault dapat terjadi saatshipdanother_shipmenunjuk keSpaceshipyang sama- setelah mengambil referensi internal string melalui
match uniq ship.engine - lalu mengganti engine yang sama ke varian lain dengan
other_ship.engine := ImpulseEngine 0x42 - kemudian memodifikasi
strlama, yang berarti memakai bagian dalam setelah kontainernya dihancurkan
- setelah mengambil referensi internal string melalui
- Karena itu, Ante perlu melarang pembuatan referensi mutable borrow ke salah satu varian ketika referensi mutable borrow tersebut menunjuk ke union
- Ini berkebalikan dengan aturan untuk struct
- Jika ada referensi
mutke struct, referensimutke field-nya bisa dibuat - Jika ada referensi
mutke union, referensimutke bagian dalam variannya tidak bisa dibuat
- Jika ada referensi
uniq dan temporary uniq conversion
uniqberarti exclusive mutable reference, yaitu referensi mutable eksklusif- Jika suatu variabel menyimpan
uniq Spaceship, itu adalah satu-satunya referensi yang dapat dipakai untukSpaceshiptersebut- Konsep ini mirip dengan
&mut Spaceshipdi Rust
- Konsep ini mirip dengan
- Untuk menangani bagian dalam union secara aman, Ante memakai temporary uniq conversion
- Aturan intinya adalah bahwa jika referensi lain yang berpotensi menjadi alias tidak dipakai dalam cakupan tertentu, maka referensi
uniqdapat diperoleh secara sementara- Dalam blok
match uniq ship.engine, akses keship.enginediperlakukan sepertiuniq - Selama blok ini, kompiler melarang penggunaan variabel lain yang sudah ada jika variabel itu dapat secara tidak langsung memuat
Spaceship
- Dalam blok
- Rust mencegah keberadaan
uniqkarena “referensi lain mungkin ada di suatu tempat”, sedangkan Ante mengizinkanuniqselama referensi-referensi itu tidak digunakan dalam cakupan tersebut - Dalam konteks ini,
uniq Spaceshipbukan benar-benar referensi yang unik secara global, melainkan satu-satunya referensi yang dapat digunakan dalam cakupan itu- Nuansanya mirip pointer
restrictdi C
- Nuansanya mirip pointer
Akses yang diizinkan dan yang ditolak
- Jika
other_ship: Rc Spaceshipdiakses di dalam cakupanmatch uniq ship.engine, seharusnya terjadi error kompilasi- karena
other_ship.enginebisa menjadi alias dariship.engine - dan perubahan pada
other_ship.enginesaatship.enginesedang dipakai dapat memicudrop
- karena
- Struct lain seperti
HasAShipyang memiliki fieldRc Spaceshipjuga ditolak dengan alasan yang samaother.ship.enginejuga dapat mencapaiSpaceshipyang sama secara tidak langsung
- Sebaliknya, bilangan bulat seperti
new_fuel: I32tetap dapat dipakai- karena
I32tidak mungkin memuat referensi keSpaceship
- karena
- Jika
Spaceshipsendiri memiliki field sepertifollow_ship: Rc Spaceship, maka kasus itu juga ditolak- Dalam situasi itu,
uniq Spaceshipdapat dicapai lagi melalui jalur internalnya sendiri, sehingga secara umum tipe rekursif tidak dapat menjalani konversimut -> uniq
- Dalam situasi itu,
Batasan pada pemanggilan dan pengembalian fungsi
- Konversi
mut -> uniqjuga dapat terjadi saat pemanggilan fungsi - Ketika
foo (var ship: Rc Spaceship) (new_res: Resonator)memanggilmaybe_use_resonator ship new_res,shipdikonversi menjadiuniq Spaceshipdi titik pemanggilan- Kompiler hanya perlu memeriksa apakah argumen lain dapat memuat referensi ke
Spaceship - Dalam contoh itu,
Resonatortidak memuat referensi semacam itu sehingga diizinkan
- Kompiler hanya perlu memeriksa apakah argumen lain dapat memuat referensi ke
- Pada pengembalian nilai, referensi
uniqhasil konversi tidak dapat dikembalikan sebagaiuniqbiasa- karena setelah fungsi mengembalikan, pemeriksaan kompiler bahwa “variabel yang bisa menjadi alias tidak dipakai dalam cakupan ini” tidak lagi berlaku
- Sebagai gantinya, tipe pengembalian dapat dinyatakan sebagai
local uniq Foo- Secara internal, saat mengonversi
mut refkeuniq ref, yang sebenarnya selalu dibuat adalah local uniq - Dalam kebanyakan kasus ini dapat dipakai seperti
uniqbiasa, tetapi saat mengembalikannya perlu dinyatakan secara eksplisit
- Secara internal, saat mengonversi
Biaya desain dan alternatifnya
- Ante dapat mengubah referensi yang dihitung referensinya seperti
Rc Spaceshipmenjadiuniq Spaceshipsementara tanpa error runtime - Kekurangannya, kompiler harus menelusuri tipe secara rekursif untuk menjawab pertanyaan seperti “apakah
Enginebisa mencapaiSpaceship?” - Analisis seperti ini bisa rapuh
- menambahkan field pada struct bisa menjadi breaking API change
- Pembuat Ante, Jake, sedang mencari cara yang lebih baik untuk mempertahankan jaminan ini
- pendekatan seperti group borrowing dan Flix references, yaitu memberi tiap tipe mutable bersama semacam tipe brand anonim yang unik
- pendekatan menambahkan effect seperti
Mutates 'asaat tipe bersama diubah agar analisis tipe tidak diperlukan - pendekatan di mana pengguna memeriksa saat runtime apakah dua referensi menunjuk ke objek yang berbeda, atau menyediakan pemeriksaan
unsafeyang dibungkus dalam API aman - pendekatan di mana kompiler melacak nilai yang tidak disimpan secara tidak langsung di dalam
Rcsehingga tidak mungkin menjadi alias
- Gagasan yang mirip dengan iso permission milik Pony, atau izin sementara yang melihat ke dalam struct tetapi melarang penggunaan referensi yang menunjuk ke luar, juga masih menjadi kemungkinan
- Bagian sulitnya adalah mempertahankan fleksibilitas ini sambil tetap menjaga target Ante yaitu kegunaan, keterbacaan, dan kesederhanaan
Arus yang lebih luas dalam keamanan memori
- Shared mutable borrowing dulu dianggap mustahil, dan sudut pandang ini menjadi salah satu landasan desain Rust
- Kini berbagai pengecualian mulai menumpuk
- Ante dapat memperoleh referensi borrow
uniqdari data shared-mutable melalui aturan local uniqueness - Vale dapat memperoleh referensi borrow immutable dari data shared-mutable melalui pure function
- group borrowing dapat membuat referensi borrow shared-mutable bahkan jika tidak shape-stable
- GhostCell di Rust memungkinkan graph objek saling menunjuk dengan bebas, tetapi pada saat tertentu hanya boleh ada satu referensi mutable ke salah satunya
- Ante dapat memperoleh referensi borrow
- Arus ini memberi isyarat bahwa mungkin ada prinsip yang lebih umum untuk menangani shared mutable borrowing dalam desain keamanan memori
Perbandingan dengan Rust Cell
- Pengguna Rust mungkin bertanya apa bedanya dengan pendekatan menaruh
Cellpada field struct - Dalam contoh Ante, dari
Rc Spaceshipdapat diperoleh referensimut Stringkestatus: Stringlalu langsung menambahkan" (refueling)" - Dengan pendekatan
Cell<String>di Rust,&mut Stringtidak bisa diperoleh dariRc<Spaceship>- Sebagai gantinya, Anda harus memasukkan nilai default sementara dengan
status_ref.replace(String::new()) - memodifikasi
Stringyang dikeluarkan - lalu mengembalikannya lagi di akhir dengan
replace(status)
- Sebagai gantinya, Anda harus memasukkan nilai default sementara dengan
- Pendekatan ini memiliki beberapa kelemahan
- perlu membuat instance default seperti
"" - ada risiko lupa memanggil
replaceyang terakhir - ada risiko seseorang membaca
statussaat nilainya masih dalam keadaan terganti
- perlu membuat instance default seperti
- Ante memungkinkan memperoleh referensi sementara ke string
status, dan selama itu kompiler memastikan kode lain tidak dapat mengaksesnya
1 komentar
Komentar di Lobste.rs
Menganggap bahwa “peminjaman mutable bersama” itu mustahil bukan sekadar pengorbanan yang diterima Rust demi mencapai tujuannya, melainkan lebih dekat dengan tujuan inti Rust itu sendiri
Sebab state mutable bersama membuat penalaran lokal terhadap kode menjadi sulit
"References are like jumps" by withoutboats membahas hal ini dengan baik. Argumennya adalah bahwa mencegah perubahan tak disengaja pada state yang memiliki alias merupakan inti untuk membuat sistem yang bekerja dengan benar menjadi lebih mudah, dan aturan lifetime Rust bukan sekadar mekanisme untuk menghindari garbage collection, melainkan struktur yang lebih mendalam untuk memastikan kemampuan bernalar dalam bahasa yang mengizinkan state mutable dan state beralias secara bersamaan
Kelihatannya cukup bagus
Jika pemahaman saya benar, keajaiban berpindah dari referensi bersama ke referensi mutable dimungkinkan karena terbatas pada tipe yang tidak dibagikan antar-thread, dan keunikan
Rctampaknya dijamin dengan memperlakukan semua objek dari tipe yang sama seolah-olah dipinjam dengan lifetime yang samaMana yang lebih baik antara sintaks eksplisit dan sintaks alami mungkin soal selera, tetapi ini menunjukkan bahwa jika compiler mengetahui lebih banyak tentang
Cell, ia dapat mengizinkan referensi mutable terhadapnya dengan lebih fleksibelDan ini juga menghindari istilah membingungkan seperti di Rust, ketika
mutdipakai seolah-olah berarti bukan mutable, melainkan eksklusif/unikuniqberarti memperoleh lock?”, tetapi saya memahaminya sebagai bahwa pembandingnya bukanArc, melainkanRcmutberarti eksklusif/unik?Saya penasaran apakah ada yang bisa menebak apa prinsip pemersatu yang diisyaratkan di bagian akhir
Diskusi-diskusi sebelumnya tentang tulisan blog antelang.org juga layak dibaca
Saya belum begitu paham cara kerjanya. Sepertinya maksudnya “jika punya pointer mutable ke sebuah objek, kita bisa memperoleh referensi perubahan ke slice dari objek itu”
Namun kalau begitu, misalnya tampaknya hal seperti
mutref someobjext = …,mutref subfield = someobjext.a.b,someobjext.a = somethingelsebisa dilakukan, dan itu bisa membuatsubfieldmenjadi tidak valid atau rusak karena nilainya berubahDi tulisannya ada banyak penjelasan, perbandingan dengan bahasa lain, dan contoh kode, tetapi saya sulit menemukan bagian yang benar-benar merangkum semantik langkah demi langkah dari perilaku ini secara mendasar