Rust: Masalah pelacakan untuk obat generik const (RFC 2000)

Dibuat pada 15 Sep 2017  ·  202Komentar  ·  Sumber: rust-lang/rust

Masalah pelacakan untuk rust-lang/rfcs#2000

Pembaruan:

Jika Anda ingin membantu, lihat masalah open const generics dan jangan ragu untuk melakukan ping ke @varkor , @eddyb , @yodaldevoid , @oli-obk atau @lcnr untuk bantuan dalam memulai!


Memblokir stabilisasi:

  • [ ] Desain:

    • [ ] Menyelesaikan pemesanan parameter const dan tipe, dengan parameter default

    • [ ] Tentukan keseimbangan biaya UX / implementasi terbaik untuk menyatukan ekspresi const abstrak.

    • [ ] Bagaimana kita menentukan bentuk ekspresi const yang baik.

  • [x] Implementasi
  • [ ] Dokumentasi

    • [ ] panduan pedesaan


Masalah implementasi yang tersisa:

  • [ ] Selesaikan berbagai komentar FIXME(const_generics) .
  • [ ] Selesaikan masalah dengan kanonikalisasi / normalisasi malas.
  • [ ] Selidiki penanganan parameter const dalam pola.
  • [ ] Tambahkan lebih banyak tes.
  • [ ] Menerapkan default untuk parameter const ( FIXME(const_generics:defaults) ).
  • [ ] Perbaiki masalah A-const-generics lainnya .
  • [ ] Audit penggunaan has_infer_types .
  • [x] Melarang ekspresi kompleks untuk argumen const yang melibatkan parameter (untuk saat ini), misalnya {X * 2} .
  • [ ] Audit diagnostik (misalnya https://github.com/rust-lang/rust/pull/76401#discussion_r484819320).
A-const-fn A-const-generics A-typesystem B-RFC-approved C-tracking-issue F-const_generics T-compiler T-lang requires-nightly

Komentar yang paling membantu

Berikut ringkasan kemajuan sejauh ini pada obat generik const.


Sebelum pengerjaan const generics dapat dimulai dengan baik, ada beberapa refactoring yang perlu dilakukan. @jplatte mengambil tugas dengan https://github.com/rust-lang/rust/pull/45930. @jplatte kemudian mulai mengerjakan implementasi utama const generics, tetapi ternyata mereka tidak punya cukup waktu untuk melanjutkan.

@yodaldevoid dan saya melanjutkan di mana @jplatte tinggalkan, tetapi dengan cepat menemukan bahwa membuat kemajuan terhalang oleh cara parameter generik ditangani secara umum. Maka dimulailah serangkaian perubahan untuk mengubah cara obat generik ditangani di seluruh basis kode: https://github.com/rust-lang/rust/pull/48149 , https://github.com/rust-lang/rust/pull /48452 , https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/51880.

Dengan begitu, implementasi const generics bisa dimulai dengan sungguh-sungguh. Sejak itu, @yodaldevoid dan saya perlahan tapi pasti secara bertahap menambahkan dukungan untuk const generics: https://github.com/rust-lang/rust/pull/58191 , https://github.com/rust-lang/rust /pull/58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang/rust/pull/59170 , https://github.com/rust-lang/rust/pull/59355 , https://github.com/rust-lang/rust/pull/59415 , https://github .com/rust-lang/rust/pull/60058 , https://github.com/rust-lang/rust/pull/60280 , https://github.com/rust-lang/rust/pull/60284 dan sebagian besar baru-baru ini https://github.com/rust-lang/rust/pull/59008. (Ini sebagian besar telah dipisahkan dari permintaan tarik generik const utama .)


Apa statusnya sekarang? Beberapa tes const generics sekarang berfungsi Namun, masih ada yang tidak, dan ada sejumlah FIXME s di seluruh kode yang masih harus kita tangani. Kami semakin dekat sekarang, dan kami berada pada tahap di mana ada beberapa buah tergantung rendah jika Anda ingin membantu.

  • Pertama, kita membutuhkan lebih banyak tes. Hanya ada beberapa tes generik const sejauh ini, tetapi kami ingin lebih banyak lagi. Saat ini, karena kami menyadari sejumlah masalah, kami tidak memerlukan laporan bug, tetapi kami perlu lulus tes. Jika Anda menemukan kasus uji yang berfungsi (dan tidak terlihat terlalu mirip dengan pengujian yang ada), jangan ragu untuk membuka permintaan tarik untuk menambahkannya.
  • Kedua, seperti yang disebutkan, ada sejumlah FIXME(const_generics) tersebar di seluruh kode. Kami berencana untuk menyelesaikannya, tetapi @yodaldevoid dan saya hanya punya banyak waktu, jadi jika Anda pikir Anda bisa mengatasinya, silakan (meskipun Anda mungkin ingin meninggalkan komentar yang mengatakan banyak, jadi kami tidak melakukannya' t upaya duplikat).

Saya telah menulis ikhtisar tentang beberapa masalah implementasi yang tersisa sebelum const generics siap untuk pengujian yang tepat, di posting teratas, untuk memberikan gambaran kasar tentang apa yang harus dilakukan.

Butuh waktu, tetapi kami terus membuat kemajuan dan berharap untuk mengikutinya. Ini memotivasi untuk akhirnya melihat hal-hal mulai jatuh ke tempatnya!

Semua 202 komentar

44275 menambahkan predikat ConstEvaluatable , dan WF([T; expr]) sekarang membutuhkan ConstEvaluatable(expr) , karena expr dievaluasi dengan malas (bahkan jika itu hanya literal integer). Memenuhi predikat ini membutuhkan ekspresi untuk dievaluasi dengan sukses, sementara normalisasi mengabaikan kesalahan dan membiarkan ekspresi Unevaluated ditemukan, tidak tersentuh, yang kurang lebih terjadi dengan proyeksi jenis terkait. Saya mengharapkan sistem yang sama untuk menskalakan ke const generik.

@EpicatSupercell telah menyatakan minatnya untuk mengerjakan ini, saya akan membimbing mereka melalui implementasi awal. Namun, kita tidak bisa melangkah terlalu jauh karena keterbatasan yang dijelaskan di #44275.

Artinya, kita membutuhkan normalisasi malas @nikomatsakis ' untuk memungkinkan ekspresi konstan yang disematkan dalam tipe untuk mengamati batas dalam ruang lingkup (dari item fungsi / definisi tipe / impl / dll.), tanpa menghasilkan dependensi siklik separuh waktu.

Titik jalan implementasi (untuk bimbingan lebih langsung, cari @eddyb di Gitter atau eddyb di IRC):

  • Deklarasi / Sintaks:
  • Deklarasi / Semantik

    • Struktur data: ty::Generics - tambahkan parameter const samping parameter tipe

    • Konversi dari HIR: generics_of - buat ty::ConstParameterDef dari hir::ConstParam

  • Gunakan / Resolusi Nama

    • Struktur data: Def - tambahkan varian untuk parameter const

    • Resolusi nama lulus: with_type_parameter_rib - mendukung kedua jenis dan const generik

  • Gunakan / Semantik
  • Kesimpulan

Perhatikan bahwa semua ini harus mengizinkan impl<T, const N: usize> Trait for [T; N] {...} , tetapi tidak benar-benar meneruskan ekspresi konstan ke tipe/fungsi, misalnya ArrayVec<T, 3> .

Saya ingin mencoba ini :)

@jplatte @eddyb Ada berita tentang ini?

@samsartor ada refactoring yang harus dilakukan sebelum implementasi utama bekerja. Itu sekarang hampir selesai (saya sedang menunggu umpan balik saat ini). Saya tidak benar-benar tahu berapa banyak pekerjaan yang ada setelah itu. Parsing const params adalah yang saya mulai pada awalnya, sebelum refactoring. Ini belum selesai, tetapi saya harus bisa menyelesaikannya dalam waktu relatif cepat.

@jplatte Akan lebih baik, jika Anda dapat menautkan permintaan tarik. Saya tidak dapat menemukannya.

Belum ada PR-nya. Semua pekerjaan saya dapat ditemukan di sini .

Kerja bagus sehingga @jplatte jauh 🍻

Sekarang ada PR untuk dasar ( Generics refactoring): #45930

Saya pikir ini sudah disertakan tetapi akan lebih baik untuk menangani pelipatan gaya C++ untuk menangani gaya aljabar linier n array dimensi dengan panjang yang bervariasi, IE a 4x4 [[f64;4];4]atau array dimensi 4x3x5x6 [[[[ f64;6];5];3];4] dan dapat membungkus dengan benar dan menghasilkan metode khusus untuk keduanya DAN implementasi sifat yang tepat untuk skalar vektor berdimensi benar, dll. Lihat https://Gist.github.com/huhlig /8b21850b54a75254be4b093551f8c2cb untuk contoh yang belum sempurna.

Saya tidak ingat siapa pun yang mengusulkan ekspresi lipatan untuk Rust sebelumnya, apalagi sebagai bagian dari RFC ini. Tetapi karena ini adalah Rust, apakah ada alasan mengapa kami tidak dapat mengimplementasikan ekspresi fold sebagai makro biasa, daripada sintaks khusus yang baru?

Apa langkah selanjutnya setelah #45930 digabungkan? @jplatte

@kjaleshire Langkah selanjutnya adalah mengimplementasikan penguraian const generics (lihat komentar @eddyb lebih lanjut). Saya sudah mulai mengerjakan ini sebelum menjadi jelas bahwa refactoring dalam refactoring akan diperlukan. Pekerjaan saya yang sudah ada yang dapat ditemukan di sini ; itu belum di-rebase sejak PR refactoring digabungkan.

@jplatte Saya yakin Anda bermaksud menyebutkan @kjetilkjeka.

Terima kasih atas pembaruannya! Saya yakin saya jauh dari satu-satunya yang menantikan fitur ini. Pertahankan kerja bagus!

@jplatte tidak ingin menjadi tidak sabar, tetapi apakah ada pekerjaan untuk ini? Apakah Anda memerlukan bantuan? Haruskah seseorang membantu?

@est31 Maaf. Tidak punya waktu untuk mengerjakan ini dalam beberapa saat, dan saya tidak sepenuhnya yakin kapan saya akan punya waktu. Mungkin lebih baik bagi orang lain untuk melanjutkan di mana saya tinggalkan. Saya pikir bagian parsing sebagian besar sudah selesai. Ada dua tempat dalam kode di mana saya tidak yakin apa yang harus dilakukan jika param generik menjadi param const:

Juga belum ada tes untuk penguraian const generics (dan untuk kode pencetakan cantik yang saya perbarui secara bersamaan). Tidak yakin informasi lain apa yang dapat saya berikan yang akan dibutuhkan/membantu orang lain untuk terus mengerjakan ini, tetapi jangan ragu untuk melakukan ping kepada saya jika ada sesuatu yang tidak jelas tentang kode saya.

@jplatte ~ None di yang pertama dan tidak ada di yang kedua~~ (cc @pnkfelix @nikomatsakis)

EDIT : tidak ada hubungannya dalam kedua kasus.

@eddyb Untuk cuplikan tertaut pertama apakah Anda yakin None harus dikembalikan? Saya mungkin tidak mengerti apa yang terjadi di sini, tetapi bagi saya tampaknya tidak ada yang harus dilakukan. Jika None dikembalikan, parameter lain yang mungkin tidak aman akan dilewati.

@yodaldevoid Maaf, Anda benar, saya tidak menyadari ini pada Generics .

Saya ingin melanjutkan ini di mana @jplatte tinggalkan. Saya telah mengerjakan ini selama beberapa hari sekarang, melalui instruksi mentoring @eddyb karena saya punya waktu hanya untuk melihat apakah saya bisa mendapatkan di mana saja. Saya sudah turun ke bagian "Gunakan / Semantik" pada saat ini. Saya mungkin akan mampir ke salah satu obrolan segera untuk mengajukan pertanyaan.

@yodaldevoid Senang mendengarnya. Saya tidak tahu tentang Gitter, tetapi Anda pasti akan mendapatkan banyak panduan tentang #rustc dan/atau #rust-internals di IRC!

@yodaldevoid : maukah Anda berkolaborasi? Saya juga telah melakukan investigasi terhadap masalah ini (😅) — mungkin kita bisa mengisi kekosongan dalam pekerjaan masing-masing. (Anda dapat melihat apa yang telah saya lakukan sejauh ini di sini .) Mungkin kita bisa mengobrol di IRC (#rustc atau #rust-internals) tentang hal itu?

@varkor Sepertinya Anda lebih maju dari yang saya dapatkan. Saya pasti akan bersedia untuk berkolaborasi. Saya akan mencoba untuk menangkap Anda di IRC di beberapa titik dan sementara itu melihat apakah saya telah menyelesaikan sesuatu yang belum Anda lakukan.

Apakah ada kemajuan dalam hal ini?
Saya sedang menulis kode untuk disematkan, dan saya benar-benar membutuhkan obat generik const.

@qwerty19106
Kemajuan sedang dibuat dalam hal ini, meskipun perlahan. @varkor dan saya telah mengerjakan ini terus

Selain mengimplementasikan const generics, kami (varkor) telah melakukan beberapa pembersihan untuk memungkinkan semua ini (lihat #48149 dan #48523). Saya percaya rencana saat ini adalah menunggu dua permintaan tarik itu untuk dilalui sebelum menarik obat generik const, tetapi varkor dapat berbicara lebih banyak tentang itu.

Saya sangat memahami kebutuhan Anda akan const generics untuk pekerjaan yang disematkan. Saya memulai ini karena saya juga sangat menginginkan const generics sehingga saya dapat membersihkan dan membuat sebagian besar kode yang disematkan aman.

TL;DR: Kemajuan sedang berjalan, tetapi ini rumit. Saya merasakan Anda di bagian depan yang tertanam.

Untuk kotak centang "dokumentasi", akan sangat bagus untuk mendapatkan sesuatu di rustc guide .

Terima kasih @yodaldevoid atas balasan Anda. Saya akan menantikan akhir pekerjaan Anda.

Update bagi yang penasaran (karena saya juga penasaran). Re: dua PR yang disebutkan di atas : #48523 telah bergabung dan #48149 terus mengalami kemajuan.

@mark-im Barang bagus! Kerja bagus oleh @varkor. Kira-kira kira-kira kapan ETA ya? :-)

Semangat, @flip111.

Sepertinya PR refactoring besar kedua #48149 digabung :)

/cc saya

Selanjutnya @varkor PR #51880

Hanya ingin memberikan pembaruan cepat, karena saya tahu beberapa orang telah bertanya tentang kemajuan dengan obat generik const.

Untuk memberikan beberapa konteks, pada bulan Maret , @yodaldevoid dan saya memiliki implementasi awal yang hampir berhasil (sebagian besar implementasi tampaknya telah selesai, dan kami baru saja membersihkan beberapa kerusakan yang tersisa). Namun, cabang yang kami kerjakan adalah pra-miri. Ketika miri digabungkan (dan di sejumlah PR berikutnya), kode untuk menangani evaluasi konstan berubah secara signifikan, yang berarti banyak dari apa yang telah kami lakukan menjadi usang.

Selain itu, diputuskan bahwa kode parameter generik perlu dibersihkan secara umum sebelum menerapkan const generik, baik untuk meningkatkan keterbacaan, tetapi juga untuk membuat kesalahan di mana kita lupa menangani const generik dalam kasus tertentu lebih sulit untuk membuat. Ini dilakukan di https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/48149 dan akan diselesaikan di https://github. com/rust-lang/rust/pull/51880. Ini sedikit lebih terlibat daripada yang saya duga sebelumnya, dan mereka membutuhkan waktu lebih lama untuk mendorong daripada yang diperkirakan.

Sementara itu, @yodaldevoid dan saya telah berupaya membuat implementasi asli kami kompatibel dengan semua perubahan selanjutnya di rustc. Butuh beberapa saat, tetapi kami sampai di sana (meskipun ada masalah abadi karena tidak pernah memiliki waktu sebanyak yang Anda harapkan). Saya harap kita akan segera mendapat kabar baik di depan itu. (Sementara itu, https://github.com/rust-lang-nursery/chalk telah membuat kemajuan yang baik, yang seharusnya mengatasi beberapa kesulitan yang awalnya dijelaskan oleh @eddyb .)


Beberapa orang bertanya bagaimana mereka dapat membantu: Saya pikir pada tahap ini, akan lebih mudah bagi kita untuk mencoba menyelesaikan implementasi awal, dan kemudian melihat bagian mana yang perlu diperhatikan. Setelah itu siap, kita akan membutuhkan banyak tes, menggunakan generik const di tempat yang berbeda (termasuk yang tidak valid, untuk pesan kesalahan), jadi itu pasti di suatu tempat yang bisa kita lakukan dengan banyak bantuan! Kami akan memberi tahu Anda ketika itu terjadi!

Maaf jika itu bukan tempat yang tepat untuk itu, tetapi saya punya saran tentang kesetaraan ekspresi const abstrak. Secara umum, ini secara langsung direduksi menjadi pengetikan yang bergantung penuh dan wilayah yang tidak dapat diputuskan. Namun, dalam karat semuanya pada akhirnya dipakai dengan nilai/tipe konkret, sehingga kami dapat menegaskan beberapa persamaan dan menunda pemeriksaannya ke instance monomorfik.

Maksud saya adalah cara yang relatif sederhana untuk memeriksa kesetaraan ekspresi const abstrak adalah sebagai berikut:

  • secara otomatis menangani kesetaraan sintaksis (mis. N+1 == N+1 harus bekerja di luar kotak)
  • memungkinkan pengguna, pada waktu definisi, untuk menambahkan persamaan seperti N+M == M+N (mungkin dalam klausa where ?). Persamaan ini dapat digunakan dengan pemeriksaan kesetaraan (menggunakan beberapa bentuk penutupan kongruensi). Definisi yang tidak mengetik cek menggunakan persamaan yang diberikan ini ditolak.
  • pada titik ekspansi monomorfik, semua persamaan dapat diperiksa dengan benar-benar menghitung konstanta exprs, yang tidak abstrak lagi. Di sini ada pilihan desain: jika persamaan yang direduksi menjadi false , mungkin ada kesalahan kompilasi ("persamaan adalah aksioma") atau sifat tidak dapat dipakai ("persamaan adalah kendala")
  • pada titik ekspansi berparametri, persamaan ini dibawa ke atas: jika Anda memiliki fungsi f diparametrisasikan oleh N mana N+0==N , persamaan ini tidak boleh hilang dalam pemanggil g karena harus diperiksa di setiap tempat monomorfik di mana g dipanggil.

Kelebihan dari metode ini adalah tidak diperlukannya pembuktian teorema, pemecah SMT, atau penulis ulang aritmatika di rustc itu sendiri. "Hanya" pemeriksaan persamaan sintaksis untuk tipe, alias modulo, dan modulo satu set persamaan yang disediakan oleh pengguna.
Kontra adalah lebih manual bagi pengguna karena persamaan yang tampak jelas seperti M+N=N+M harus ditambahkan secara eksplisit.

Contoh:

/// this works directly because of syntactic checks
fn add_end<T:Copy, const N: usize>(a: &[T;N], b: T) -> [T;N+1] {
  let x : [T;N+1] = [b;N+1];
  x[0..N] = a;
  x
}

/// this should work already
fn append<T:Copy, const M:usize, const N:usize>(a: &[T;M], b: &[T;N])
  -> [T;{M+N}]
{ … }

/// Here the equation M+M==N must be carried over or checked whenever this function is used
fn sum_pairwise_append<const M: usize, const N: usize>(a: &[i32, M],b: &[i32;N])-> [T;N]
  where M+M == N {
  let mut res : [i32; N] = append(a,a);
  for i in 0 .. N { res[i] += b[i] };
  res
} 


fn main() {
  let a: [i32; 2] = [1;2];
  let b: [i32; 4] = [2;4];
  let _ = sum_pairwise_append(a, b);  // need to check 2+2=4 
}

Persamaan sebagai aksioma

Ini berarti bahwa e1 == e2 harus selalu benar, jadi ini sebenarnya bukan batasan where tetapi lebih seperti assert_eq!(e1,e2) yang dapat digunakan oleh pemeriksa tipe. Di sini pelaksana membuat janji bahwa ini selalu benar, dan memaparkan penggunanya pada kesalahan kompilasi jika mereka memberikan parameter yang menyangkal persamaan.

Persamaan sebagai kendala

Di sini klausa where e1 == e2 adalah batasan yang harus dipenuhi untuk keberhasilan penggunaan sifat/fungsi parametris ini. Itu berarti e1 == e2 tidak harus selalu berlaku, yang mungkin menarik untuk persamaan yang hanya berlaku untuk domain, seperti (x*y) / z == (y*x) / z yang akan gagal untuk membuat instance z==0 .

@c-cube Kami sudah memiliki sistem untuk "klausa where implisit", setidaknya untuk fungsi, yang diturunkan dari " aturan WF (berbentuk baik), yaitu jika Anda mendefinisikan fn foo<'a, 'b>(x: &'a &'b i32) {} maka pemanggil harus memenuhi 'b: 'a (sebagai akibat dari membutuhkan WF(&'a &'b i32) -> &'b i32: 'a -> 'b: 'a ).

Kita dapat menggunakan kembali sistem itu (sebenarnya ini sudah diterapkan) untuk mendorong batasan yang diberikan oleh tanda tangan (dari bentuk "ekspresi konstan ini berhasil dievaluasi") hingga pemanggil, sedangkan apa pun yang hanya digunakan di dalam tubuh masih memerlukan where klausa

Sebagian besar hal lain yang Anda gambarkan tampaknya mendekati apa yang direncanakan (termasuk bentuk "kesetaraan sintaksis"), tetapi kami tidak ingin pemeriksaan "waktu monomorfisasi", kami dapat memiliki cara (baik implisit atau melalui where klausa) untuk "menyebarkan kendala ke instantiator", dan kemudian kami menghasilkan kesalahan di mana kendala "masih terlalu umum" (jadi tidak diketahui apakah mereka berlaku), atau sebaliknya jika kami dapat mengevaluasi mereka dan mereka tidak berlaku.

Hampir semua hal lain yang Anda gambarkan tampaknya mendekati apa yang direncanakan

@eddyb apakah Anda tahu posting referensi yang membahas rencana ini?

@ flip111 Mungkin beberapa RFC. Mungkin @withoutboats lebih tahu.

# 51880 selesai: tada:: tada:

@withoutboats @oli-obk Apa pendapat Anda tentang memblokir penyatuan ekspresi yang tepat seperti N + 1 (dari misalnya [T; N + 1] ) untuk mendapatkan beberapa bentuk dasar evaluasi simbolis di miri ?

Saya pikir kita sudah memiliki mekanisme untuk menyandikannya, ketika @varkor menambahkan ConstValue::{Infer,Param} , kita dapat menggunakannya untuk melacak nilai "(dangkal) valid tetapi tidak diketahui" (simbolis), dan kemudian juga memasukkan nilai (terutama integer) operasi dan panggilan di atas itu.

Setiap keputusan aliran kontrol non- assert masih memerlukan nilai yang diketahui, tetapi assert itu sendiri mungkin dapat direifikasi sebagai AssertThen(condition, assert message, success value) .
(Baik condition dan success value bisa menjadi simbol!)

Mampu mengekspor nilai simbolis ke dalam ty::Const berarti kita dapat menerapkan beberapa penyatuan di luar miri, memperlakukan (sebagian besar?) operasi sebagai buram.

Kita harus berhati-hati untuk tidak mengasumsikan variabel inferensi apa pun, tetapi misalnya N + 1 Saya pikir kita dapat mendukung penyatuan dua Add(Param(N), Bits(1)) bersama-sama.

@varkor Kasus uji yang saya pikir harus bekerja hanya dengan normalisasi malas, tanpa penyatuan:

/*const*/ fn append<const N: usize, T>(xs: [T; N - 1], x: T) -> [T; N] {
    let new = MaybeUninit::<[T; N]>::uninitialized();
    unsafe {
        let p = new.as_mut_ptr() as *mut T;
        (p as *mut _).write(xs);
        p.add(N - 1).write(x);
    }
    new.into_inner()
}

Itu hanya akan berhasil karena:

  • N - 1 muncul di level tipe tepat satu kali

    • semuanya bisa merujuk pada ekspresi itu, tidak jelas

    • ( solusi potensial : type ArrayWithoutLast<T, const N: usize> = [T; N - 1]; )

  • itu dalam posisi argumen di dalam tanda tangan

    • (tidak ingat apakah posisi pengembalian juga akan berfungsi. @nikomatsakis?)

    • penelepon bisa membuktikan itu WF, dengan memberikan nilai konkret untuk N

    • "menggelembungkan" persyaratan WF membutuhkan penyatuan / ArrayWithoutLast

    • kegunaan lain akan membutuhkan "penyematan dalam klausa where " misalnya [T; N - 1]: Sized



      • bahkan dapat menyalahgunakan where [T; N - 1]: (apakah itu diabaikan hari ini? aduh!)


      • lagi melewati penyatuan dengan where ArrayWithoutLast<T, N>: ...



Jadi secara keseluruhan, kami mungkin dapat mengandalkan aturan WF untuk memaksa "validasi" ekspresi const ke pengguna, tetapi kami masih menginginkan penyatuan untuk hal-hal lain (termasuk tidak memerlukan peretasan seperti ArrayWithoutLast ).

53645

Pertanyaan kecil yang berawal dari pembahasan Satuan Ukuran. Apa yang harus kita lakukan jika tipe generik di atas konstanta tidak secara langsung bergantung padanya? Sebagai contoh:

struct Foo<T, const N: usize> {
    a: T,
    // Will such array be "the way" to handle this problem?
    // Or do we want some kind of `std:marker::PhantomConstData`? (which can be a simple
    // type alias to the same array)
    // Or maybe make `PhantomData` to accept constants?
    _phantom: [(), N],
}

_hantom: [(), N],

Saya berasumsi maksud Anda [(); N] , tetapi ini hanya akan berfungsi untuk usize .

Saya pikir masuk akal untuk memiliki marker::PhantomConst memungkinkan jenis const .

Hmm… Membaca ulang RFC dengan mengingat komentar terbaru, saya bertanya-tanya: apakah hal seperti ini diperbolehkan?

struct Foo<T, const V: T> {
    _phantom: PhantomConst<T, V>,
}

Saya tidak bisa melihatnya dilarang di mana pun, tetapi saya pikir itu layak mendapatkan setidaknya contoh.

@Ekleog : sejauh yang saya ketahui, tipe parameter const mungkin tidak bergantung pada parameter tipe pada awalnya (meskipun pasti masuk akal untuk ekstensi). (Implementasinya lebih rumit jika kita mengizinkannya, jadi masuk akal untuk memulai dengan versi yang paling sederhana.)

Bagaimana perkembangannya dalam hal ini? Apakah kita tahu perkiraan waktu kapan ini akan terjadi setiap malam?

@Zauberklavier Segera setelah permintaan tarik ini selesai. Mengutip darinya:

Ada jalan yang panjang untuk dilalui

@newpavlov Saya berasumsi parameter konstan akan diizinkan tanpa digunakan di bidang.
Alasan parameter tipe harus digunakan adalah karena varians, tetapi konstanta tidak memiliki masalah ini.

@eddyb itu yang saya ingat dari diskusi RFC juga.

@varkor Jadi ini berarti PhantomConst diusulkan oleh @cuviper tidak dapat eksis dalam kondisi RFC saat ini… kan? meskipun komentar terbaru tampaknya menunjukkan bahwa bagaimanapun juga tidak perlu PhantomConst .

Apakah parameter const memengaruhi varians parameter tipe yang mereka libatkan?

Saat ini saya percaya obat generik const tidak dapat memiliki parameter tipe dalam tipenya.

Saya tidak jelas tentang apa yang dapat dilakukan oleh versi kerja pertama ini.

Misalnya apakah kode dalam komentar ini akan dikompilasi:
https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717

Jika kode itu dikompilasi, itu akan menjadi cara untuk menegakkan batasan untuk konstanta sebelum mendapatkan sintaks untuknya.

@rodrimati1992 Anda mungkin bisa melakukan (): IsTrue<{N < 128}> tanpa tipe terpisah.
Saya kira Anda ingin menggunakan kembali batasan dan membuatnya benar-benar berfungsi? (karena menyalin ekspresi N < 128 tidak akan berhasil, awalnya)
Anda bisa menggunakan trait Lt128<const N: usize> = IsTrue<{N < 128}>; , saya kira.
Ada juga opsi untuk hanya menulis where [(); 128 - N], , saya pikir (tapi saya tidak yakin kami menjamin itu akan panik).

@rodrimati1992 Anda mungkin bisa melakukan (): IsTrue<{N < 128}> tanpa tipe terpisah.
Saya kira Anda ingin menggunakan kembali batasan dan membuatnya benar-benar berfungsi? (karena menyalin ekspresi N < 128 tidak akan berhasil, awalnya)
Anda bisa menggunakan trait Lt128<const N: usize> = IsTrue<{N < 128}>; , saya kira.
Ada juga opsi untuk hanya menulis where [(); 128 - N], , saya pikir (tapi saya tidak yakin kami menjamin itu akan panik).

Jadi, dengan alias sifat yang bisa saya tulis ulang adalah ini?:

trait AssertLessThan128<const N:usize>=
    Assert<{N<=128}, (
        Str<"uint cannot be constructed with a size larger than 128,the passed size is:",
        Usize<N>>
    ) >;

Ide dengan Assertsifat menggunakan kesalahan jenis untuk mencetak pesan kesalahan yang disematkan dalam suatu jenis, yang dalam kasus AssertLessThan128 adalah:

(Str<"uint cannot be constructed with a size larger than 128,the passed size is:",Usize<N>>)

yang saya harapkan lebih bermanfaat daripada where [(); 128 - N], , karena ini memberi tahu Anda dalam pesan kesalahan mengapa kesalahan waktu kompilasi terjadi.

Anda juga dapat melakukan if !(N < 128) { panic!("...") } secara konstan, menurut saya?

Saya pikir arsitektur std::fmt tidak akan ada sampai batasan const trait (atau yang serupa) tiba?

Dengan hal ini Anda dapat memiliki pesan kesalahan dengan banyak nilai di dalamnya (tertanam dalam jenis).

Mungkin lebih baik menunggu const generics untuk membicarakan hal ini, karena akan lebih mudah untuk menunjukkan contoh yang dapat dieksekusi.

@rodrimati1992 lihat https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.md , akan ada kasus khusus untuk mendapatkan const panic!() sebelum itu dimungkinkan melalui fitur lain (dengan tebakan itu mungkin tidak akan mengizinkan pemformatan nilai khusus pada awalnya).

@rodrimati1992 Ahh, begitu, itu memang trik yang cerdas!

Ini statusnya apa?

Langkah selanjutnya masih https://github.com/rust-lang/rust/pull/53645 AFAIK.

Ini benar. Untuk memberikan pembaruan cepat bagi mereka yang tidak mengikuti PR #53645, const generics telah dikompilasi dan bekerja di setidaknya satu usecase sederhana. Yang tersisa adalah menyelesaikan codegen untuk kasus penggunaan lain, termasuk generik const di arrys, dan beberapa pembersihan keluaran kesalahan. Setelah itu, PR harus siap bergabung dan orang-orang dapat mulai memainkannya.

Mungkin di luar topik, tetapi apakah ini akan memungkinkan varian atau garpu Potongan dan metode terkait untuk memiliki Item menjadi array ukuran tetap waktu kompilasi alih-alih sepotong? Ini akan memungkinkan loop for untuk merusak/mengikat ke pola tak terbantahkan yang memetakan elemen dalam chunk ke variabel, seperti for (first, second) in arr.chunks(2) yang saat ini gagal, dan saya kira (tanpa pembenaran apa pun diakui) bahwa itu akan memungkinkan lebih banyak pengoptimalan dalam kasus penggunaan tertentu.

@jeffvandyke Anda mungkin memikirkan ChunksExact secara khusus. Perubahan seperti itu akan melanggar API, jadi itu tidak bisa dilakukan. Kami dapat menambahkan API baru yang memberikan referensi array di masa mendatang, tetapi kami tidak dapat merusak yang sudah ada.

@jeffvandyke Anda mungkin memikirkan ChunksExact secara khusus. Perubahan seperti itu akan melanggar API, jadi itu tidak bisa dilakukan. Kami dapat menambahkan API baru yang memberikan referensi array di masa mendatang, tetapi kami tidak dapat merusak yang sudah ada.

Saya hanya menunggu ini diimplementasikan dan distabilkan sebelum mengusulkan PR yang menambahkan API semacam itu selain ChunksExact dan variannya :)

Varian runtime dan waktu kompilasi keduanya memiliki kasus penggunaannya, Anda tidak selalu tahu ukuran chunk Anda sebelumnya. Dari segi pengoptimalan, jika Anda menggunakan ChunksExact dengan konstanta, itu harus kurang lebih sama menurut pengujian saya. Kompiler dapat mengoptimalkan semua pemeriksaan batas.

untuk diimplementasikan dan distabilkan

Saya sarankan tidak menunggu untuk stabilisasi sebagai API seperti itu akan menjadi salah satu penggunaan yang lebih baik untuk membantu latihan fitur ini untuk stabilisasi.

Saya menduga ini belum berfungsi untuk blok impl ? Saya mencoba

#![feature(const_generics)]

struct The<const Val: u64>();

impl<const Val: u64> The<Val> {
    fn the() {
        println!("{}", Val);
    }
}

tetapi, seperti yang telah diperingatkan, kompilernya mogok dengan

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: rustc::util::common::panic_hook
   5: std::panicking::rust_panic_with_hook
   6: std::panicking::continue_panic_fmt
   7: rust_begin_unwind
   8: core::panicking::panic_fmt
   9: core::slice::slice_index_order_fail
  10: rustc_resolve::Resolver::resolve_ident_in_lexical_scope
  11: rustc_resolve::Resolver::resolve_path
  12: rustc_resolve::Resolver::resolve_path_without_parent_scope
  13: rustc_resolve::Resolver::smart_resolve_path_fragment
  14: rustc_resolve::Resolver::smart_resolve_path
  15: <rustc_resolve::Resolver<'a> as syntax::visit::Visitor<'tcx>>::visit_ty
  16: syntax::visit::walk_generic_args
  17: syntax::visit::walk_ty
  18: rustc_resolve::Resolver::with_generic_param_rib
  19: rustc_resolve::Resolver::resolve_item
  20: rustc_resolve::Resolver::resolve_crate
  21: rustc::util::common::time
  22: rustc_interface::passes::configure_and_expand_inner
  23: rustc_interface::passes::configure_and_expand::{{closure}}
  24: <rustc_data_structures::box_region::PinnedGenerator<I, A, R>>::new
  25: rustc_interface::passes::configure_and_expand
  26: <rustc_interface::queries::Query<T>>::compute
  27: <rustc_interface::queries::Query<T>>::compute
  28: <rustc_interface::queries::Query<T>>::compute
  29: rustc_interface::queries::<impl rustc_interface::interface::Compiler>::prepare_outputs
  30: rustc_interface::interface::run_compiler_in_existing_thread_pool
  31: <std::thread::local::LocalKey<T>>::with
  32: <scoped_tls::ScopedKey<T>>::set
  33: syntax::with_globals

Kesalahan terkait dengan const di impl<const Val: u64> karena menghapus bagian itu menyebabkan kesalahan lain tetapi tidak macet.

tetapi alat peraga untuk Rust bahkan untuk mempertimbangkan fitur ini. Saya tidak tahu apakah itu akan berhasil tetapi sintaksnya tampak alami, saya melakukannya, dan lo rustc mengatakan itu ada :)

Saya tidak terkejut bahwa itu tidak berfungsi, karena fitur yang sangat ditunggu-tunggu ini masih ditelusuri melalui kompiler saat kita berbicara (lihat misalnya #59008 dan #58581 dan pekerjaan sebelumnya pada #53645 yang ditinggalkan karena PR terlalu besar , tetapi masih tetap terbuka sebagai pelacak untuk mengumumkan kemajuan).

Namun, saya tidak yakin apakah akses irisan di luar batas harus diharapkan dari rintisan implementasi saat ini. @varkor @yodaldevoid , bisa lihat?

Ya, peringatannya benar: const generics belum berfungsi dalam bentuk apa pun. Masih ada beberapa permintaan tarik lagi sebelum mereka siap untuk mulai bermain-main.

Maaf jika ini bukan tempat yang tepat untuk mengajukan pertanyaan, tetapi saya tidak dapat menemukan tempat yang lebih baik. Hanya 2 pertanyaan:

  1. Bisakah satu fungsi menjadi const bersyarat ? Misalnya, suatu fungsi dapat memiliki 2 tanda tangan seperti:

    const fn foo<A: const T>(x: T)  // `A` const implements `T`
    fn foo<A: T>(x: A)              // `A` implements `T` normally
    

    Dalam hal ini, foo adalah const iff A: const T ; jika A tidak const mengimplementasikan T , itu masih memenuhi batas tetapi foo tidak lagi const. Penting juga bagi penulis untuk dapat menentukan batas generik apa pun untuk contoh yang lebih kompleks (mis. where A::Output : Bar ). Contoh yang bagus dari ini bahkan aritmatika sederhana:

    // This only accepts types that const implement `T`
    const fn square_const<T: const Mul>(x: T) -> T {
      x*x
    }
    
    // This accepts types that implement `T` any way, but it is not const
    // This function behaves identically to `square_const`
    // But has a different signature and needs a different name
    fn square<T: Mul>(x: T) -> T {
      square_const(x)
    }
    
    let a: u8 = 5;
    let b: FooNumber = FooNumber::new();
    square_const(a); // `u8` const implements Mul
    square(b); // `FooNumber` implements `Mul` normally, so we need a separate function?
    

    Saya merasa sangat yakin bahwa pasti ada cara untuk melakukan ini, dan saya terkejut bahwa itu tidak disebutkan dalam RFC (kecuali saya melewatkannya?).

  2. _[kurang penting]:_ Apakah akan ada cara untuk mendeteksi di dalam tubuh fungsi const apakah kita berjalan pada waktu kompilasi atau waktu berjalan? Saya pikir beberapa makro yang mirip dengan cfg!() akan menjadi ekstensi yang berguna, jika tidak ada alasan lain selain debugging. cfg!() saat ini sudah dievaluasi pada waktu kompilasi, jadi saya pikir (/ tebak) bahwa itu harus dapat mengetahui apakah fungsi tersebut digunakan sebagai const atau fungsi biasa karena itu juga ditentukan pada kompilasi -waktu. Namun ini kurang penting daripada pertanyaan pertama saya.

@Coder-256

  1. Ya, lihat https://github.com/rust-lang/rfcs/pull/2632.
  2. Saya tidak yakin apakah itu mungkin, meskipun mengingat hal di atas, saya juga tidak yakin itu perlu.

@Coder-256 Kedua pertanyaan ini tidak terkait dengan const generik, melainkan fungsi const. Generik const adalah untuk menjadi generik di atas consts (misalnya foo<2>() ) daripada fungsi yang dapat dijalankan pada waktu kompilasi. Saya membayangkan inilah mengapa Anda tidak menemukan jawaban atas pertanyaan Anda di RFC 2000.

@rpjohnst Terima kasih, tapi saya pikir saya mungkin tidak jelas. Saya telah melihat rust-lang/rfcs#2632 dan rust-lang/rfcs#2000, tetapi saya cukup yakin bahwa ini juga tidak disebutkan. (tapi saya bisa saja salah?) Yang saya tanyakan adalah fungsi const bersyarat . Lihat contoh yang saya tulis karena sulit untuk dijelaskan.

@yodaldevoid Ups Anda benar, di mana saya harus menanyakan ini?

Adapun pertanyaan makro, saya setuju bahwa tidak banyak gunanya sekarang setelah saya memikirkannya

Yang saya tanyakan adalah fungsi const bersyarat. Lihat contoh yang saya tulis karena sulit untuk dijelaskan.

Definisi square_const dapat digunakan sebagai pengganti square (yaitu: dipaksa ke fungsi dengan tanda tangan yang setara saat run-time). Lihat https://github.com/rust-lang/rfcs/pull/2632 untuk diskusi tentang perilaku ini (juga, utas itu adalah tempat yang tepat untuk mengajukan pertanyaan tentang interaksi apa pun antara const fn dan batas sifat).

@varkor Saya tidak yakin itu masalahnya (karena batas sifat berubah), tetapi saya akan bertanya di rust-lang/rfcs#2632.

Laporan Kerusakan:

Kode:

#![feature(const_generics)]

use std::marker::PhantomData;

struct BoundedU32<const LOWER: u32, const UPPER: u32> {
    value: u32,
    _marker: PhantomData<(LOWER, UPPER)>,
}

Penyusun:

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.35.0-nightly (719b0d984 2019-03-13) running on x86_64-unknown-linux-gnu

note: compiler flags: -C codegen-units=1 -C debuginfo=2 --crate-type lib

note: some of the compiler flags provided by cargo are hidden

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

@Jezza : const generics belum sepenuhnya diimplementasikan dan diharapkan tidak berfungsi. Kami akan membuat pengumuman saat tiba waktunya untuk mulai bereksperimen dengan #![feature(const_generics)] (yang akan diberitahukan kepada Anda jika Anda berlangganan masalah ini).

@varkor Tunggu @Jezza memiliki _marker: PhantomData<(LOWER, UPPER)> - itu adalah dua parameter const digunakan sebagai tipe, mengapa rustc_resolve menghasilkan kesalahan?

Poin bagus: Saya akan menyelidiki ini. Perhatikan bahwa ini hanya masalah dengan #![feature(const_generics)] , jadi ini bukan masalah kritis (menggunakan const non-generik menghasilkan kesalahan seperti yang diharapkan). Mungkin tidak pernah mencapai rustc_resolve .

~ @eddyb : ICE ini berasal dari resolve_ident_in_lexical_scope , jadi saya membayangkan itu mungkin terkait dengan https://github.com/rust-lang/rust/issues/58307.~

Sunting: sebenarnya, mungkin tidak — yang tampaknya hanya berlaku untuk macro_rules! .

Diminimalkan:

#![feature(const_generics)]

struct S<const C: u8>(C);

Selesaikan ICE sebelum menghasilkan kesalahan "tipe yang diharapkan, nilai yang ditemukan".

Indeks di luar batas di sini:
https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919

Nightly saat ini menghasilkan "parameter N tidak pernah digunakan" untuk kode berikut:

struct Foo<const N: usize> { }

Dari diskusi sebelumnya saya pikir kompilasi harus menerima kode ini. Apakah itu hanya artefak dari implementasi yang belum selesai?

@newpavlov parameter tipe normal perlu digunakan, haruskah seseorang dapat melakukan PhantomData<n> ?

Saya kira itu sudah mungkin untuk dilakukan PhantomData<[(); N]> . Tidak yakin itu sesuatu yang sebenarnya ingin kami terapkan, karena AFAIU titik PhantomData adalah untuk menandai varians, dan AFAIU tidak ada gagasan varians wrt. parameter generik const.

Dan itu hanya berfungsi ketika N bertipe usize .

Kami memutuskan bahwa tidak perlu menggunakan parameter const selama diskusi RFC dan implementasi saat ini adalah bug.

@withoutboats Bisakah Anda menunjukkan di mana di RFC yang dinyatakan? Saya mencoba menemukan sesuatu untuk efek itu dan pasti melewatkannya.

saya tidak tahu apakah itu termasuk dalam teks RFC

@withoutboats Maukah Anda menunjukkan di mana itu dibahas? Saya menjelajah melalui RFC PR, tetapi itu tidak melompat ke arah saya.

@yodaldevoid Komentar ini dirujuk sebelumnya: https://github.com/rust-lang/rust/issues/44580#issuecomment -419576947 . Tapi itu tidak ada di RFC PR, lebih pada masalah ini.

Saya tidak dapat menelusuri riwayat komentar dari beberapa tahun yang lalu sekarang, tetapi saya dapat menjelaskan: menggunakan variabel tipe diperlukan sebagai pemblokir untuk membuat Anda berpikir tentang varians dari parameter tersebut (IMO ini juga tidak perlu dan kami dapat default ke kovarian, tapi itu masalah terpisah). Parameter const tidak memiliki interaksi dengan varians, jadi ini tidak memiliki motivasi.

@HadrienG2 Terima kasih telah menemukan komentar yang relevan.

@withoutboats Saya tidak benar-benar mengharapkan Anda untuk menjelajahi semua tahun komentar untuk ini, itu hanya harapan bahwa Anda sudah memilikinya.

Terima kasih atas penjelasannya. Saya harus mengakui bahwa saya tidak pernah bisa membungkus pikiran saya di sekitar varians tidak peduli berapa kali saya mencoba untuk mempelajarinya, tetapi bahkan tanpa itu ketika memikirkan lagi tidak memerlukan penggunaan parameter biaya masuk akal. Saya akan membuang perbaikan bug ini ke daftar FIXME kami.

Berikut ringkasan kemajuan sejauh ini pada obat generik const.


Sebelum pengerjaan const generics dapat dimulai dengan baik, ada beberapa refactoring yang perlu dilakukan. @jplatte mengambil tugas dengan https://github.com/rust-lang/rust/pull/45930. @jplatte kemudian mulai mengerjakan implementasi utama const generics, tetapi ternyata mereka tidak punya cukup waktu untuk melanjutkan.

@yodaldevoid dan saya melanjutkan di mana @jplatte tinggalkan, tetapi dengan cepat menemukan bahwa membuat kemajuan terhalang oleh cara parameter generik ditangani secara umum. Maka dimulailah serangkaian perubahan untuk mengubah cara obat generik ditangani di seluruh basis kode: https://github.com/rust-lang/rust/pull/48149 , https://github.com/rust-lang/rust/pull /48452 , https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/51880.

Dengan begitu, implementasi const generics bisa dimulai dengan sungguh-sungguh. Sejak itu, @yodaldevoid dan saya perlahan tapi pasti secara bertahap menambahkan dukungan untuk const generics: https://github.com/rust-lang/rust/pull/58191 , https://github.com/rust-lang/rust /pull/58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang/rust/pull/59170 , https://github.com/rust-lang/rust/pull/59355 , https://github.com/rust-lang/rust/pull/59415 , https://github .com/rust-lang/rust/pull/60058 , https://github.com/rust-lang/rust/pull/60280 , https://github.com/rust-lang/rust/pull/60284 dan sebagian besar baru-baru ini https://github.com/rust-lang/rust/pull/59008. (Ini sebagian besar telah dipisahkan dari permintaan tarik generik const utama .)


Apa statusnya sekarang? Beberapa tes const generics sekarang berfungsi Namun, masih ada yang tidak, dan ada sejumlah FIXME s di seluruh kode yang masih harus kita tangani. Kami semakin dekat sekarang, dan kami berada pada tahap di mana ada beberapa buah tergantung rendah jika Anda ingin membantu.

  • Pertama, kita membutuhkan lebih banyak tes. Hanya ada beberapa tes generik const sejauh ini, tetapi kami ingin lebih banyak lagi. Saat ini, karena kami menyadari sejumlah masalah, kami tidak memerlukan laporan bug, tetapi kami perlu lulus tes. Jika Anda menemukan kasus uji yang berfungsi (dan tidak terlihat terlalu mirip dengan pengujian yang ada), jangan ragu untuk membuka permintaan tarik untuk menambahkannya.
  • Kedua, seperti yang disebutkan, ada sejumlah FIXME(const_generics) tersebar di seluruh kode. Kami berencana untuk menyelesaikannya, tetapi @yodaldevoid dan saya hanya punya banyak waktu, jadi jika Anda pikir Anda bisa mengatasinya, silakan (meskipun Anda mungkin ingin meninggalkan komentar yang mengatakan banyak, jadi kami tidak melakukannya' t upaya duplikat).

Saya telah menulis ikhtisar tentang beberapa masalah implementasi yang tersisa sebelum const generics siap untuk pengujian yang tepat, di posting teratas, untuk memberikan gambaran kasar tentang apa yang harus dilakukan.

Butuh waktu, tetapi kami terus membuat kemajuan dan berharap untuk mengikutinya. Ini memotivasi untuk akhirnya melihat hal-hal mulai jatuh ke tempatnya!

Saya sudah menunggu berbulan-bulan untuk posting ini @varkor :) Saya bukan penyihir Rust tapi saya ingin menangani salah satu dari FIXME s

Selamat @jplatte , @yodaldevoid dan @varkor. Ini adalah langkah besar untuk menghilangkan ciri khusus Array-like di lib matematika.

Posting silang...

@varkor Mengenai tes, mungkin akan membantu untuk mengetahui apa yang diharapkan berfungsi. Misalnya, saya terkejut bahwa ini tidak berfungsi: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=d84ffd15226fcffe02c102edb8ae5cf1

Juga, untuk referensi mereka yang tertarik dengan FIXME: https://oli-obk.github.io/fixmeh/

@mark-im mencoba menempatkan {} sekitar FOO . Dengan cara itu akan berhasil.

Mengutip https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md :

Saat menerapkan ekspresi sebagai parameter const (kecuali untuk larik), yang bukan merupakan ekspresi identitas, ekspresi harus berada di dalam blok. Pembatasan sintaksis ini diperlukan untuk menghindari keharusan melihat ke depan tanpa batas saat mengurai ekspresi di dalam suatu tipe.

{expression} seharusnya hanya diperlukan jika ekspresi bukan pengidentifikasi atau literal, itu adalah bug.

Mengutip RFC yang sama:

Ekspresi identitas: Ekspresi yang tidak dapat dievaluasi lebih lanjut kecuali dengan menggantinya dengan nama dalam ruang lingkup. Ini termasuk semua literal serta semua ident - misalnya 3, "Halo, dunia", foo_bar.

@mark-im Ya, saat ini kami tidak dapat membedakan antara idents untuk tipe dan consts saat pertama kali diurai. Kami memutuskan keputusan tentang bagaimana menangani semua itu di masa depan. Saya kira sekarang mungkin masa depan.

Untuk sedikit latar belakang, kami telah berbicara tentang dua cara yang saya ingat tentang bagaimana menangani ini. Cara pertama adalah memaksa orang untuk menandai argumen const (baik dengan mengetikkan const sebelum mereka atau dengan cara lain). Ini tidak bagus dari sudut pandang ergonomis, tetapi mudah dari sudut pandang penguraian. Cara kedua adalah memperlakukan semua argumen generik sebagai hal yang sama sampai kita mulai memasangkannya dengan parameter generik nanti selama kompilasi. Ini mungkin metode yang ingin kami ambil, dan sudah ada beberapa pekerjaan untuk memungkinkan ini, kami hanya belum mengambil lompatan terakhir.

Pekerjaan yang luar biasa. Saya senang membantu, dengan memperbaiki beberapa FIXME , jika saya bisa. Saya tidak punya banyak pengalaman dalam basis kode rustc sama sekali, jadi saya akan mulai pada FIXME https://github.com/rust-lang/rust/blob/master/src/librustc_mir/monomorphize/item.rs# L397 , sepertinya akan mudah.

Bagaimana dengan kode berikut:

trait Foo {
    const N: usize;
    fn foo() -> [u8; Self::N];
}

Saat ini menghasilkan "tidak ada item terkait bernama N ditemukan untuk kesalahan kompilasi tipe Self dalam lingkup saat ini". Apakah kode tersebut akan diterima setelah menyelesaikan FIXME s atau akan memerlukan upaya tambahan untuk diterapkan?

@yodaldevoid

Pertanyaan singkat, maaf jika ini sudah pernah dibahas.

Cara kedua adalah memperlakukan semua argumen generik sebagai hal yang sama sampai kita mulai memasangkannya dengan parameter generik nanti selama kompilasi. Ini mungkin metode yang ingin kami ambil, dan sudah ada beberapa pekerjaan untuk memungkinkan ini, kami hanya belum mengambil lompatan terakhir.

Apakah ini tidak berjalan berlawanan dengan prinsip Rust membuat tanda tangan fungsi eksplisit untuk menghindari menghasilkan kesalahan yang berhubungan dengan implementasi fungsi? Mungkin saya benar-benar salah paham tentang ini dan Anda berbicara tentang penguraian parameter generik di dalam tubuh fungsi.

Apakah ini tidak berjalan berlawanan dengan prinsip Rust membuat tanda tangan fungsi eksplisit untuk menghindari menghasilkan kesalahan yang berhubungan dengan implementasi fungsi? Mungkin saya benar-benar salah paham tentang ini dan Anda berbicara tentang penguraian parameter generik di dalam tubuh fungsi.

Ini tentang menentukan apakah pengidentifikasi yang diteruskan sebagai parameter umum ke suatu fungsi adalah konstanta atau tipe.

Contoh:

fn greet<const NAME:&'static str>(){
    println!("Hello, {}.",NAME);
}
const HIS_NAME:&'static str="John";
greet::<HIS_NAME>();
greet::<"Dave">();

Perhatikan bahwa ketika mendefinisikan fungsi, Anda harus menentukan bahwa NAME adalah konstanta, tetapi ketika Anda memanggilnya, tidak perlu mengatakan bahwa HIS_NAME adalah konstanta di dalam operator turbofish ( ::< > ) .

@zesterer Jika solusi yang diusulkan ini dibuat transparan bagi pengguna (misalnya ketika mendefinisikan suatu fungsi tidak ada perbedaan antara tipe parameter), Anda akan benar. Namun ide di balik solusi yang diusulkan bukanlah untuk mengubah perilaku yang dihadapi pengguna, melainkan hanya mengubah implementasinya. Pengguna masih akan menulis tanda tangan fungsi dengan tipe eksplisit, const, dan parameter seumur hidup, dan argumen umum masih perlu dicocokkan dengan parameter yang sesuai, hanya bagaimana kita menguraikannya di kompiler akan berubah.

@newpavlov Saya terkejut bahwa kode tidak berfungsi. Jika Anda bisa mengirimkan masalah terpisah itu akan dihargai.

@yodaldevoid @robarnold

Ah, mengerti. Saya salah berasumsi ini terkait dengan tanda tangan tipe/fungsi.

Untuk menggunakan const generics pada tipe array bawaan, saya telah membuka #60466 untuk melihat pendapat orang lain tentang ini.

Blok impl tampaknya benar-benar rusak saat ini. Apakah ini diharapkan dalam keadaan fitur saat ini, atau haruskah saya membuka masalah lain tentangnya?

Blok impl tampaknya benar-benar rusak saat ini. Apakah ini diharapkan dalam keadaan fitur saat ini, atau haruskah saya membuka masalah lain tentangnya?

Anda perlu menambahkan {} s di sekitar N , impl<const N: usize> Dummy<{N}> {} .

Tapi ya, Anda akan mendapatkan kesalahan tentang parameter yang tidak dibatasi.

Terima kasih. Lupakan tentang kawat gigi!

Saya tidak terkejut bahwa itu gagal setelah diselesaikan, karena ini adalah kasus uji yang sangat diminimalkan.

...tapi ya, yang ini mungkin harus bekerja:

#![feature(const_generics)]

trait Dummy {}

struct Vector<const N: usize> {
    data: [f32; N],
}

impl<const N: usize> Dummy for Vector<{N}> {}

... dan masih gagal dengan E0207:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:12
  |
9 | impl<const N: usize> Dummy for Vector<{N}> {}
  |            ^ unconstrained const parameter

error: aborting due to previous error

For more information about this error, try `rustc --explain E0207`.
error: Could not compile `small-matrix`.

To learn more, run the command again with --verbose.

dan saya berasumsi inilah yang dimaksud @varkor dengan "masalah dengan penanganan array untuk consts generik" di #60466:

#![feature(const_generics)]

fn dummy<const N: usize>() -> [f32; N] {
    [0.; N]
}

->

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:3:44
  |
3 |   pub fn dummy<const N: usize>() -> [f32; N] {
  |  ____________________________________________^
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:4:5
  |
4 |     [0.; N]
  |     ^^^^^^^

error: internal compiler error: QualifyAndPromoteConstants: Mir had errors
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) ("return type"): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) (LocalDecl { mutability: Mut, is_user_variable: None, internal: false, is_block_tail: None, ty: [type error], user_ty: UserTypeProjections { contents: [] }, name: None, source_info: SourceInfo { span: src/lib.rs:3:1: 5:2, scope: scope[0] }, visibility_scope: scope[0] }): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:356:17
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.36.0-nightly (cfdc84a00 2019-05-07) running on x86_64-unknown-linux-gnu

note: compiler flags: -C debuginfo=2 -C incremental --crate-type lib

note: some of the compiler flags provided by cargo are hidden

@HadrienG2
Lihat #60619 dan #60632.

Saya pikir kode ini harus dibuat:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e45b7b5e881732ad80b7015fc2d3795c

tetapi saat ini kesalahan:

error[E0119]: implementasi sifat yang bertentangan std::convert::TryFrom<[type error]> untuk tipe MyArray<_, _> :

Sayangnya itu adalah batasan Try{From,Into} dan tidak ada hubungannya dengan const generics. Mereka tidak dapat diimplementasikan secara umum dalam banyak kasus: playpen

@oberien dalam contoh Anda, T adalah tipe asing, dan tidak diketahui apakah T: Into<Foo<T>> berlaku atau tidak.

Dalam contoh saya, [T; N] adalah tipe yang didefinisikan dalam libcore, dan #[fundamental] (tbh saya bahkan tidak tahu apa artinya ini), jadi saya pikir penyelesai sifat benar-benar tahu [T; N]: Into<MyArray<T, {N}>> tidak berlaku, dan di sana seharusnya tidak menjadi konflik, saya pikir?

[...] dan #[fundamental] [...]

Ini masalahnya, fundamental terkait dengan mengizinkan pengguna hilir untuk menentukan implementasi sifat ketika mereka membungkus tipe lokal dalam tipe pembungkus dasar. Anda dapat melihat di taman bermain ini bahwa implementasi Anda berfungsi di antara tipe non-fundamental, tetapi gagal jika tipe from ditandai fundamental (dengan pesan kesalahan yang jauh lebih baik).

@varkor @yodaldevoid Jadi, saya baru saja berakhir dalam situasi di mana S<{N == 0}> (dengan S mengambil parameter const bool dan N a const usize ) bukan S<{true}> atau S<{false}> di mata kompiler (dalam arti bahwa ia tidak mengimplementasikan sifat yang sama dengan keduanya), dan saya tidak yakin apakah ini bug atau keterbatasan yang diharapkan dari prototipe saat ini. Bisakah Anda memberi saya penyegaran cepat tentang logika penyatuan saat ini?

Sebagai contoh yang diminimalkan, ini ...

// Machinery for type level if-then-else
struct Test<const B: bool>;
trait IfFn<S, Z> { type Out; }
impl<S, Z> IfFn<S, Z> for Test<{true }> { type Out = S; }
impl<S, Z> IfFn<S, Z> for Test<{false}> { type Out = Z; }

// Returns an u8 if B is true, else an u16
fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
    0
}

... menghasilkan kesalahan berikut:

error[E0277]: the trait bound `Test<B>: IfFn<u8, u16>` is not satisfied
  --> src/lib.rs:32:1
   |
32 | / fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
33 | |     0
34 | | }
   | |_^ the trait `IfFn<u8, u16>` is not implemented for `Test<B>`
   |
   = help: the following implementations were found:
             <Test<false> as IfFn<S, Z>>
             <Test<true> as IfFn<S, Z>>

Saya sebenarnya tidak tahu, tetapi saya pikir masalahnya mungkin karena kompiler tidak memiliki logika untuk memutuskan apakah 0 literal adalah u8 atau u16 . Coba sesuatu seperti: 0u8 as _

Bukan itu, pesan kesalahannya tetap sama.

Tetapi jika Anda menginginkan versi yang tidak melibatkan inferensi tipe integer, inilah minimalisasi yang lebih konyol:

struct Test<const B: bool>;
trait Not { const B: bool; }
impl Not for Test<{true }> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

Masih gagal karena Test<{B}> diduga tidak mengimplementasikan Not .

Hai ! Saya tidak begitu yakin apakah penyatuan pada nilai const seharusnya berfungsi atau tidak, tetapi ada sistem penyatuan baru dalam karya ("kapur") yang sedang dikerjakan tetapi bukan yang digunakan rustc saat ini.
Jadi apa yang Anda coba lakukan _mungkin_ harus menunggu sampai kapur siap. Dan bahkan kemudian, saya tidak yakin itu akan berhasil atau seharusnya bekerja bahkan dengan kapur.
Lihat https://github.com/rust-lang/rust/issues/48049 untuk kemajuan.

@HadrienG2 bisakah Anda mengajukan masalah terpisah untuk itu? Saya tidak sepenuhnya terkejut bahwa itu tidak berfungsi saat ini karena beberapa masalah lain yang sedang kami cari, tetapi ini adalah kasus baru dan saya ingin melacaknya.

@carado Unifikasi dilakukan pada consts atau semua ini tidak akan berhasil. Obat generik Const tidak diblokir pada kapur.

@HadrienG2 setelah kami memiliki spesialisasi untuk const generics, Anda akan dapat melakukan sesuatu seperti

struct Test<const B: bool>;
trait Not { const B: bool; }
impl<const B: bool> Not for Test<B> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

Apa yang ingin Anda lakukan adalah semacam pemeriksaan kelengkapan seperti ada untuk kecocokan yang baru saja diperluas ke sistem sifat. Tidak tahu itu disebutkan di utas mana pun.

@yodaldevoid Diarsipkan sebagai https://github.com/rust-lang/rust/issues/61537 .

@carado Chalk tidak menyangkut "primitif sistem tipe" yang sebenarnya, yang salah satunya.
Artinya, rustc masih harus (bahkan hari ini, karena Chalk sudah terintegrasi sebagian) mengimplementasikan bagian dari unifikasi yang berhubungan dengan entitas yang buram terhadap Kapur (seperti dua ekspresi konstan yang berbeda).
Jika kita menerapkan itu (yang kita tidak memiliki rencana dalam waktu dekat, bagaimanapun), itu tidak akan banyak berubah bahkan setelah Kapur menggantikan sistem sifat (yang merupakan tujuan utamanya, bukan penyatuan).

Oh, burukku kalau begitu! Saya kira saya tidak benar-benar tahu apa yang saya bicarakan.

Salah satu hal luar biasa yang akan disediakan oleh const generics adalah mengkompilasi fungsi-fungsi sederhana yang dihitung waktu, misalnya faktorial.

Contoh:

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<i - 1>().unwrap() + i)
    }
}

Sejauh yang saya mengerti, ada beberapa dukungan terbatas untuk obat generik const di malam hari? Jika demikian, apakah ada tempat yang lebih teratur daripada masalah ini di mana saya dapat menemukan cara menggunakannya dan sebagainya?

@dancojocaru2000 const fungsi harus menjadi cara yang lebih disukai untuk perhitungan tingkat nilai pada waktu kompilasi, misalnya https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4994b7ca9cda0bfc44f5359443431378 , yang terjadi tidak berfungsi karena belum sepenuhnya diterapkan. Tetapi const generik.

Cuplikan Anda mungkin bermasalah karena saya tidak yakin apakah match seharusnya berfungsi untuk argumen const . Anda juga mencampuradukkan penjumlahan dengan perkalian dalam kode, tetapi itu tidak terlalu menjadi masalah.

Saya pikir Anda sudah dapat melakukan faktorial pada waktu kompilasi dengan pengkodean Peano, yang sering ditampilkan sebagai demo untuk bahasa fungsional lainnya.

waktu kompilasi menghitung fungsi sederhana

Saya pikir fitur yang lebih tepat adalah const fn .

Secara teoritis, kode Anda akan berfungsi hampir apa adanya

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Tapi itu tidak:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

Meskipun Anda benar-benar harus menggunakan tipe yang tidak ditandatangani:

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

apakah ada tempat yang lebih terorganisir daripada masalah ini di mana saya dapat menemukan cara menggunakannya dan lainnya?

Itu biasanya ditutupi oleh buku yang tidak stabil , tetapi tidak ada yang berguna di sana sekarang. Saat Anda menemukan sedikit, mungkin Anda dapat mempertimbangkan untuk mulai membuat sketsa beberapa konten untuk itu?

faktorial pada waktu kompilasi dengan pengkodean Peano,

Untuk contohnya, lihat peti typenum

Secara teoritis, kode Anda akan berfungsi hampir apa adanya

Saat memanggil factorial::<0> _ => factorial::<{X - 1}>() * X akan dikodekan juga kan? Tetapi mencoba melakukannya akan menyebabkan underflow integer.

Bisakah saya mengharapkan kode seperti ini untuk dikompilasi di masa mendatang?

#![feature(const_generics)]

trait NeedsDrop<const B: bool> { }
impl<T> NeedsDrop<std::mem::needs_drop<T>()> for T { }

fn foo<D: NeedsDrop<false>>(d: D) { }

Ini bersinggungan dengan implementasi baru-baru ini dari beberapa array [T; N] untuk N <= 32 sebagai const generics, tetapi kode berikut tidak dikompilasi pada terbaru ( 4bb6b4a5e 2019-07-11 ) setiap malam dengan berbagai kesalahan the trait `std::array::LengthAtMost32` is not implemented for `[u64; _]`

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32, 
   [u64; N*2]: std::array::LengthAtMost32;

meskipun berikut ini:

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32;

Sepertinya N*2 bukan const atau qualifier yang valid untuk constraint tersebut

@shamatar N*2 harus diapit dengan kurung kurawal seperti `[u64; {T*2}]

Saya pikir kesalahan "sifat tidak diterapkan" berasal dari makro turunan yang tidak menambahkan batas [u64; {N*2}] , tetapi jika turunan dihapus, saat ini ada ICE:

```kesalahan: kesalahan kompiler internal: tipe konstan memiliki kesalahan yang diabaikan: TooGeneric
-> src/main.rs:5:1
|
5 | / pub struct BigintRepresentation<
6 | | const N: gunakan
7 | | >(pub [u64; N])
8 | | di mana [u64; N]: std::array::LengthAtMost32,
9 | | [u64; {N*2}]: std::array::LengthAtMost32;
| |_______________________________________________^

utas 'rustc' panik 'tidak ada kesalahan yang ditemukan meskipun delay_span_bug dikeluarkan', src/librustc_errors/lib.rs:366:17
catatan: jalankan dengan variabel lingkungan RUST_BACKTRACE=1 untuk menampilkan backtrace.

Kode ini tidak dikompilasi:

#![feature(const_generics)]

struct Foo<const X: usize>([u8; X]);

impl<const X: usize> Foo<X> {
    fn new() -> Self {
        Self([0u8; X])
    }
}
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0573]: expected type, found const parameter `X`
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^
  |                          |
  |                          not a type
  |                          help: try using the variant's enum: `regex_syntax::ast::HexLiteralKind`

error[E0107]: wrong number of const arguments: expected 1, found 0
 --> src/lib.rs:5:22
  |
5 | impl<const X: usize> Foo<X> {
  |                      ^^^^^^ expected 1 const argument

error[E0107]: wrong number of type arguments: expected 0, found 1
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^ unexpected type argument

error: aborting due to 3 previous errors

@npmccallum Anda perlu melakukan Foo<{X}>

@pengowen123 Ya, itu mengkompilasi ( constant in type had an ignored error: TooGeneric ) tanpa derive , tetapi kemudian mungkin menjadi pertanyaan terpisah apakah "batasan ganda" seperti itu yang efektif N <= 32 dan N <= 16 tidak diterapkan oleh kompiler.

Ekspresi yang menyebutkan parameter mungkin tidak akan berfungsi untuk waktu yang lebih lama, [T; N] dan Foo<{N}> adalah kasus khusus untuk tidak memiliki ekspresi dengan penyebutan N di dalamnya, melainkan langsung merujuk ke parameter N , melewati masalah yang lebih besar.

Menggunakan Self menyebabkan crash.
Bahkan menukarnya dengan Value<{C}> , itu masih macet.

#![feature(const_generics)]

struct Value<const C: usize>;

impl<const C: usize> Value<{C}> {
    pub fn new() -> Self {
        unimplemented!()
    }
}

pub fn main() {
    let value = Value::new();
}

Sama seperti #61338, sumber masalahnya adalah kompilasi tambahan.

waktu kompilasi menghitung fungsi sederhana

Saya pikir fitur yang lebih tepat adalah const fn .

Secara teoritis, kode Anda akan berfungsi hampir apa adanya

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Tapi itu tidak:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

Meskipun Anda benar-benar harus menggunakan tipe yang tidak ditandatangani:

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Fitur ini berguna bagi saya juga, const fn tidak cukup. Saya ingin menggunakannya untuk array N-dimensi dengan kondisi penghentian dimensi=0.

Saya dapat membuat struct:

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

Ini gagal karena saya sebenarnya tidak dapat menginisialisasi array karena:

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

Saya sudah mencoba mengatasinya dengan membuat nilai const disetel ke nilai generik.

Itu melewati kesalahan di atas, tetapi kompiler kemudian macet.

error: internal compiler error: src/librustc/ty/subst.rs:597: const parameter `height/#0` (Const { ty: usize, val: Param(height/#0) }/0) out of range when substituting substs=[]

Bahasa sangat membutuhkan generik konstan dasar, sehingga perpustakaan standar tidak harus mendefinisikan setiap fungsi untuk setiap ukuran array. Ini adalah satu-satunya alasan saya tidak menggunakan karat. Apakah kita benar-benar membutuhkan turing fungsi waktu kompilasi lengkap?
Saya mungkin salah tetapi ekspresi integer sederhana sudah cukup dan saya harap tidak ada yang membuang waktu mereka, memastikan bahwa contoh-contoh aneh ini berfungsi.

Bahasa sangat membutuhkan generik konstan dasar, sehingga perpustakaan standar tidak harus mendefinisikan setiap fungsi untuk setiap ukuran array

Sudah ada beberapa upaya di sana https://github.com/rust-lang/rust/issues/61415 .

Saya harap tidak ada yang membuang waktu mereka, memastikan bahwa contoh-contoh aneh ini berhasil.

Beberapa orang melakukannya, saya tidak dapat melihat masalah dengan itu;)

Ini adalah satu-satunya alasan saya tidak menggunakan karat.

Ini adalah alasan paling tidak menarik yang pernah saya lihat dikutip untuk seseorang yang tidak menggunakan karat. Apakah Anda yakin mengatakan yang sebenarnya?

tetapi ekspresi integer sederhana sudah cukup

Membuat cakupan yang dikurangi itu sangat sulit. Cobalah tangan Anda, kami memiliki orang-orang yang benar-benar mampu membuat ini dan ada alasan mengapa butuh waktu lama.

Sayangnya, saya tidak dapat mendedikasikan banyak waktu untuk memperbaiki bug const generik baru-baru ini (atau lebih umum Rust). Jika ada yang ingin terlibat dalam mendorong const generics bersama, saya akan dengan senang hati menawarkan saran untuk mengatasi masalah terbuka dan meninjau perbaikan bug, meskipun mungkin perlu beberapa saat sebelum saya dapat berkonsentrasi pada ini lagi.

Saya harap tidak ada yang membuang waktu mereka, memastikan bahwa contoh-contoh aneh ini berhasil.

Tidak ada, miri sudah jauh lebih kuat dari C++ constexpr .
Dan tidak ada yang mengerjakan sesuatu yang mewah seperti menurunkan N = M dari N + 1 = M + 1 .

Sebagian besar bug ini bukan tentang ekspresi di dalamnya, tetapi tentang sistem tipe dan bagaimana generik const berinteraksi dengan semua fitur lainnya.
Mereka akan tetap ada jika yang Anda miliki hanyalah const generik dan literal integer.

Btw, saya pikir kesalahan "panjang array tidak dapat bergantung pada parameter generik" untuk ekspresi [expr; N] tidak diperlukan, kita dapat menggunakan trik yang sama seperti yang kita lakukan untuk tipe [T; N] , dan tarik keluar N tanpa mengevaluasinya sebagai ekspresi.

Meskipun saya ingin mencoba ini, saya tidak yakin saya orang yang tepat. Saya telah menggunakan sedikit karat dan tahu sedikit teori kompiler. Saya mungkin membutuhkan sedikit pelatihan yang adil, tetapi saya pasti bersedia. 😄

Sunting: Saya memiliki sedikit pengalaman dalam perangkat lunak secara umum.

@varkor , saya telah mencari sesuatu yang berguna untuk dilakukan pada rustc , dan saya ingin melangkah dan membantu. Juga, terima kasih telah berterus terang tentang kemampuan Anda untuk mengalokasikan waktu untuk itu!

@varkor , saya sebenarnya mengikuti komentar di atas, jadi jika Anda punya saran, saya siap! Jangan ragu untuk merujuk saya ke saluran komunikasi lain juga.

Satu hal yang bisa kita lakukan sekarang, saya baru sadar adalah mengizinkan ekspresi Foo::<{...}> / [expr; ...] untuk merujuk ke parameter generik (di bagian ... ).

Ini karena ekspresi harus bersarang di suatu tempat di dalam tubuh, dan itu cenderung mencegah ketergantungan siklik.

Namun, aku khawatir bahwa memiliki misalnya [(); [0; 1][0]] di tanda tangan akan istirahat, jadi kami mungkin akan membutuhkan kawah run pula (dan saat ini mengambil mereka selamanya).

Bagi siapa saja yang tertarik untuk membantu dengan const generics, saran saya adalah melihat daftar masalah open const generics dan menyelidiki mana yang terlihat menarik bagi Anda. Beberapa sudah melakukan penyelidikan, yang seharusnya ada di komentar. Sebagian besar masalah mungkin akan membutuhkan sedikit penggalian. Sangat membantu untuk memberikan komentar jika Anda berencana untuk menyelidiki sesuatu, jadi kami tidak menduplikasi upaya (tetapi Anda sering dapat mengabaikan penerima tugas masalah jika tidak ada aktivitas untuk sementara waktu). Jika Anda memiliki pertanyaan, Anda dapat bertanya di komentar masalah, atau di Discord atau Zulip. @eddyb , @yodaldevoid , @oli-obk dan saya mengenal banyak bidang yang relevan dan merupakan orang yang baik untuk ditanyakan. Terima kasih untuk semua minat Anda!

cc @hameerabbasi , @ranweiler

Pertanyaan tentang obat generik const:

  1. Di mana saya dapat menemukan dokumentasi (sehingga saya tidak perlu bertanya di sini)
  2. Batasan apa yang ada saat ini dalam menggunakan parameter const tipe arbitrer?
  3. Apa fitur penting dari const generik yang tidak diimplementasikan/menghancurkan kompilator?

PS (Kontributor :) Terima kasih banyak telah mengerjakan fitur ini.

const-generics masih dalam pengembangan sehingga belum ada dokumen resmi tentangnya. Saya tidak terlalu yakin dengan dua pertanyaan lainnya. Ketika saya terakhir menggunakan const-generics mereka mogok ketika saya menentukan beberapa parameter const-generic, tetapi sudah hampir sebulan sejak saya melakukan sesuatu dengannya sehingga semuanya mungkin telah berubah sejak saat itu.

Dan tidak ada yang mengerjakan sesuatu yang mewah seperti menurunkan N = M dari N + 1 = M + 1 .

Ini akan sangat berguna untuk memiliki pemecah untuk kendala tipe seperti itu. Misalnya ketika menerapkan penambahan jika dua nomor N-bit, ukuran kembali harus (N + 1) untuk memperhitungkan bit overflow. Ketika (misalnya) dua angka 5-bit diberikan, pemecah harus memeriksa bahwa N + 1 = 6 . Semoga ini bisa dibaut ke obat generik const nanti :)

@flip111 Ya, saya pikir rencananya adalah menambahkan ini nanti, tetapi batasan ekspresi umum semacam ini sangat kompleks dan sulit untuk diterapkan. Jadi kita mungkin tidak melihat mereka setidaknya selama beberapa tahun.

Sejujurnya, memecahkan masalah berbasis kendala semacam itu terdengar seperti pekerjaan untuk Prolog. Mungkin membonceng mesin kapur adalah pilihan? Afaik itu memecahkan Sifat, bukan?

Btw saya suka topiknya, meskipun saya tidak mampu membantu dengan atm ini. Terima kasih kepada semua orang yang bekerja di Const Generics atas waktu dan komitmen Anda. 💐

Pembaruan kecil: Saya sedang mengerjakan pohon Rust. Saya memang berencana untuk berkontribusi tetapi ingin belajar sendiri ke tempat di mana saya tidak membutuhkan pelatihan yang berlebihan.

Saya dapat membuat struct:

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

Ini gagal karena saya sebenarnya tidak dapat menginisialisasi array karena:

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

Saya mengalami masalah yang sama, tetapi menemukan solusi menggunakan MaybeUninit :
https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=3100d5f7a4efd844954a6fa5e8b8c526
Ini jelas hanya solusi untuk mendapatkan array yang diinisialisasi dengan benar, tetapi bagi saya ini cukup sampai cara yang tepat tersedia.
Catatan: Saya pikir kode harus selalu berfungsi sebagaimana dimaksud tetapi jika seseorang menemukan bug dengan penggunaan yang tidak aman, saya akan dengan senang hati memperbaikinya.

@raidwas Anda menjatuhkan memori yang tidak diinisialisasi ketika Anda menggunakan operator = untuk menginisialisasi memori yang tidak diinisialisasi. Lakukan ini sebagai gantinya,

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5d962ce7c553e850030240244608ec00

@KrishnaSannasi terima kasih, tangkapan bagus :D (secara teknis saya tidak menjatuhkan memori yang tidak diinisialisasi karena saya hanya menggunakannya untuk primitif, tetapi bagus untuk memiliki versi yang benar di sini)

Secara teknis, menjatuhkan pelampung tidak ditentukan, tetapi saat ini tidak dapat dieksploitasi.

Secara teknis, menjatuhkan pelampung tidak ditentukan, tetapi saat ini tidak dapat dieksploitasi.

Saya berharap bahwa menjatuhkan jenis Copy pun, bahkan yang tidak diinisialisasi, adalah larangan. Mungkin itu layak untuk diubah.

Secara teknis, menjatuhkan pelampung tidak ditentukan, tetapi saat ini tidak dapat dieksploitasi.

Saya berharap bahwa menjatuhkan jenis Copy pun, bahkan yang tidak diinisialisasi, adalah larangan. Mungkin itu layak untuk diubah.

Secara teknis masih UB. Meskipun menghapus nilai Copy ditentukan dengan aman adalah tidak boleh, kompilator mungkin memutuskan untuk melakukan beberapa optimasi tak terduga jika Anda mencoba menghapus memori yang tidak diinisialisasi dari jenis apa pun (mis. menghapus semua kode yang mungkin menyentuh nilai itu), yang mungkin bisa merusak barang. Itu pemahaman saya tentang itu.

Bukan untuk tidak sopan, tetapi 60 orang mendapatkan pemberitahuan tentang komentar di utas ini, jadi kami mungkin harus menjaga diskusi di luar topik seminimal mungkin. Mari kita gunakan ini untuk mengoordinasikan pekerjaan pada const generics karena itulah yang kita semua inginkan terjadi.

Hancur jika saya menggunakan _aliases menunjuk untuk mengetik dengan const-param_ dari peti lain (ketergantungan).

Sebagai contoh:

  • peti A, lib
    ```#![crate_type = "lib"]

    ![fitur(const_generics)]

jenis pub Alias= Struktur;
struktur pub(T);

- crate B

peti luar peti_a;
gunakan crate_a::Alias;

pub fn inner_fn(v: Alias) {}
```
crash-log (44580).txt

@fzzr- terdengar seperti #64730

Saya telah mengganti beberapa kode ke const generik dan sepertinya ada dua kasus penggunaan yang berbeda. Saya tidak yakin apakah mereka harus digabungkan atau apakah kita akan lebih baik menggunakan dua sintaks yang berbeda untuk kasus penggunaan.

Banyak penggunaan saya sebenarnya bukan untuk tipe di mana nilai konstan berperan dalam menentukan tipe, melainkan untuk memanfaatkan fitur yang dikunci untuk nilai non-const/literal (masih belum sepenuhnya didukung, misalnya pola kecocokan, tetapi pada akhirnya akan perlu).

IMHO kita harus secara resmi mendaratkan "argumen const" di samping const generics, sehingga orang tidak akan menulis kode mutan yang memperkenalkan seribu "const generics" (satu untuk setiap argumen) untuk membuat kompiler mengevaluasi variabel tertentu sebagai literal/konstanta.

@mqudsi Bisakah Anda memberikan contoh? Sudah ada rencana dan pekerjaan dasar yang sedang berlangsung untuk membuat evaluasi lebih kuat. Itu ortogonal untuk const generik.

Maksud saya adalah, jika Anda ingin memfaktorkan ulang kode parser tipikal seperti berikut ini untuk digunakan kembali:

let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
    match src.get_u8() {
        d<strong i="6">@b</strong>'0'..=b'9' => {
            result = result*10 + (d - b'0') as u32;
            digits += 1;
        },
        b'>' => match result {
            0..=191 => break, // valid range
            _ => return Err(DecoderError::OutOfRange),
        },
        _ => return Err(DecoderError::Malformed)
    }
}

Anda biasanya akan memindahkannya ke suatu fungsi, kecuali yang berikut ini tidak akan berfungsi karena nilai tertentu yang digunakan dalam pencocokan pola telah menjadi variabel:

#[inline(always)]
fn read_str_digits<B: bytes::buf::Buf>(src: &mut B, stop_word: u8, 
    max_digits: u8, min_value: u32, max_value: u32) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="10">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

Jika const generics mendarat sebelum argumen const, saya tiba-tiba dapat menyalahgunakan const generics untuk menghasilkan yang berikut:

#[inline(always)]
fn read_str_digits<const MinValue: u32, const MaxValue: u32, const StopWord: u8, B: bytes::buf::Buf>
(src: &mut B, max_digits: u8) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="14">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            StopWord => match result {
                MinValue..=MaxValue => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

Sampai hari ini, bahkan kode ini tidak akan berfungsi karena kompiler tidak mendeteksi nilai generik const sebagai konstanta yang valid untuk digunakan dalam suatu pola, tetapi itu jelas salah dan perlu ditangani sebelum RFC 2000 dapat mendarat. Jangan salah paham, saya telah berjuang untuk konstanta generik selama bertahun-tahun dan saya memiliki PR yang siap digunakan untuk selusin peti utama (saya tidak sabar untuk membuat zona waktu di chrono menjadi const generik dan menyatukan semua berbagai jenis DateTime ), tetapi jika memungkinkan untuk menyalahgunakan const generics untuk memalsukan argumen const (di mana dengan "const" yang sebenarnya kami maksud adalah "literal") maka Anda akan melihat tersebar luas penyalahgunaan itu.

Ini belum tentu akhir dunia, tetapi tanpa menggali terlalu dalam ke dalamnya, sepertinya implementasi generik const yang tepat dan lengkap tentu akan mencakup semua pipa untuk argumen const, jadi sebaiknya kita mengambil waktu ekstra untuk menyelesaikan sintaks/ux/story untuk argumen const saat kita melakukannya dan menghindari era kode yang tidak menyenangkan. Ya, hal di atas masih dapat dilakukan dengan makro, tetapi ergonomi const generics seribu kali lebih mudah.

fwiw, ini adalah bagaimana saya membayangkan versi argumen const terlihat seperti:

#[inline(always)]
fn read_str_digits<B: Bytes>(src: &mut B, min_value: const u32, 
    max_value: const u32, stop_word: const u8, max_digits: u8) -> Result<u32, ()> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="23">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(()),
            },
            _ => return Err(())
        }
    }

    ...
}

Ini hampir identik dengan const generik tetapi untuk semantik.

Anda biasanya akan memindahkannya ke suatu fungsi, kecuali yang berikut ini tidak akan berfungsi karena nilai tertentu yang digunakan dalam pencocokan pola telah menjadi variabel:

Ini sepertinya kasus penggunaan yang lebih mudah ditangani dengan mengizinkan nilai variabel terikat untuk digunakan dalam pola (bukan hanya konstanta), dengan beberapa sintaks baru. Fakta bahwa const saat ini diizinkan dalam pola adalah kontra-intuitif dan, sejauh yang saya ketahui, historis.

@mqudsi Maafkan saya jika ini pertanyaan konyol, tapi saya tidak melihat ada yang salah dengan contoh yang Anda berikan. Sepertinya kasus penggunaan yang sangat valid untuk const generik bagi saya: memiliki definisi yang digeneralisasi untuk bekerja dengan nilai arbitrer sebagai maks/min. Saya tidak benar-benar melihat manfaat argumen const dibandingkan obat generik const. Mereka tampak setara dengan saya; yaitu, argumen const hanya bisa diimplementasikan sebagai desugaring ke const generik. Bisakah Anda menguraikan lebih lanjut tentang apa yang salah dengan pola desain ini?

Berikut ringkasan pekerjaan pada const generics sejak pembaruan terakhir . Terakhir kali, itu semua tentang implementasi inti: memastikan semuanya cocok dan mendapatkan beberapa kasus uji dasar yang lulus. Sejak itu, upaya telah difokuskan untuk membuat const generics lebih andal: memperbaiki kasus yang membuat kompilator mogok, atau tidak berfungsi secara tidak terduga, atau meningkatkan diagnostik. Kami semakin dekat dengan sesuatu yang bekerja dengan andal, meskipun masih ada jalan yang harus ditempuh.


  • @ skinny121 telah melakukan pekerjaan yang fantastis dalam sebulan terakhir untuk memperbaiki berbagai masalah luar biasa dengan obat generik const:

    • Memperluas test suite (https://github.com/rust-lang/rust/pull/60550)

    • Memperbaiki masalah inferensi jenis (https://github.com/rust-lang/rust/pull/64679, https://github.com/rust-lang/rust/pull/65579)

    • Mendukung lebih banyak tipe dalam const generics, termasuk string dan slice (https://github.com/rust-lang/rust/pull/64858) dan pointer (https://github.com/rust-lang/rust/pull/64986 ) (meskipun perhatikan bahwa ini kemungkinan tidak akan segera distabilkan bersama dengan obat generik const lainnya, karena tidak didukung di RFC asli )

    • Memperbaiki masalah diagnostik dengan obat generik const (https://github.com/rust-lang/rust/pull/65154, https://github.com/rust-lang/rust/pull/65579)

    • Memperbaiki sejumlah masalah dengan penggunaan const generics cross-crate (https://github.com/rust-lang/rust/pull/65365)

  • @eddyb memperbaiki beberapa masalah dengan evaluasi const yang memengaruhi obat generik const (https://github.com/rust-lang/rust/pull/63497)
  • @matthewjasper memperbaiki beberapa masalah dengan menggunakan generik const di makro (https://github.com/rust-lang/rust/pull/63083)
  • @davidtwco memperbaiki masalah yang melibatkan const generik dan konstruktor (https://github.com/rust-lang/rust/pull/60839)
  • @GuillaumeGomez memperbaiki tampilan generik const di Rustdoc (https://github.com/rust-lang/rust/pull/61605)
  • @varkor memperbaiki beberapa masalah inferensi jenis (https://github.com/rust-lang/rust/pull/61570, https://github.com/rust-lang/rust/pull/60742, https://github. com/rust-lang/rust/pull/60508), masalah yang membatasi tempat const generik dapat digunakan (https://github.com/rust-lang/rust/pull/60717) dan berbagai crash kompiler (https:/ /github.com/rust-lang/rust/pull/61380, https://github.com/rust-lang/rust/pull/61333, https://github.com/rust-lang/rust/pull/60710 )

Selain itu, pekerjaan lain pada kompiler akhirnya memperbaiki beberapa masalah lain yang berkaitan dengan obat generik const.

Kami juga mulai menggunakan const generics di dalam compiler itu sendiri: implementasi sifat array sekarang menggunakan const generics berkat karya @crlf0710 dan @scottmcm (https://github.com/rust-lang/rust/pull/60466, https ://github.com/rust-lang/rust/pull/62435), yang menghasilkan peningkatan kinerja , dan yang juga akan memungkinkan kami untuk membatasi implementasi sifat array di masa mendatang (ketika const generics distabilkan). @Centril menggunakan pendekatan yang sama untuk meningkatkan VecDeque (https://github.com/rust-lang/rust/pull/63061).


Banyak bug yang sangat umum dengan const generics telah diperbaiki dalam beberapa bulan terakhir. @nikomatsakis sedang menyelidiki lazy normalization , yang seharusnya memperbaiki sejumlah masalah yang tersisa, sementara @jplatte sedang mencari cara untuk memperbaiki disambiguasi parameter const dari parameter tipe (jadi Anda tidak perlu lagi mengetik {X} untuk a argumen konstan).

Saya juga ingin mengucapkan terima kasih kepada @eddyb atas semua bimbingan dan ulasan mereka untuk obat generik const sepanjang pengembangan, yang sangat berharga.

Masih ada cukup banyak masalah lain yang harus diatasi dan, seperti sebelumnya, kami dapat menggunakan semua bantuan yang bisa kami dapatkan — jika Anda tertarik untuk mengatasi masalah yang tersisa , silakan ping saya tentang masalah tersebut, atau di Discord atau Zulip sebagai petunjuk untuk memulai.

@mqudsi @mark-im Apakah pantas untuk memperluas alternatif sintaksis saat ini untuk obat generik, impl Trait dalam posisi argumen, ke const obat generik? Ini adalah sintaks yang disarankan @mqudsi untuk kasus penggunaan ini.

Secara pribadi saya pikir ini akan meningkatkan keterbacaan kasus penggunaan seperti itu, tetapi saya melihat satu masalah: obat generik normal digunakan untuk meneruskan data, jadi mereka terikat pada argumen. Ini tidak berlaku untuk obat generik const, jadi bagaimana Anda akan meneruskannya? Berpura-pura mereka adalah argumen?
Dalam kasus impl Trait dalam posisi argumen, itu juga tidak dapat digunakan dengan sintaks turbofish. Untuk const dalam argumen, itu akan menjadi kebalikannya. Ini mungkin membingungkan.

Saya tidak yakin apakah manfaatnya lebih besar daripada "keanehan" di sini, tetapi saya tetap ingin membagikan pemikiran saya.

Samar-samar saya mengingat diskusi tentang ini sebelumnya, dan sekarang saya telah menemukannya: https://internals.rust-lang.org/t/pre-rfc-const-function-arguments/6709

@PvdBerg1998 itu sepertinya ide yang sangat buruk. Saya benar-benar tidak mengerti bahwa <…> adalah _sintaks keras_ untuk dibaca. Dua kemungkinan di sini:

  1. Anda berpikir, seperti yang saya lakukan, bahwa itu tidak sulit untuk dibaca, bahkan dengan beberapa batasan.
  2. Anda pikir itu sulit untuk dibaca: maka mungkin solusinya adalah dengan menempatkannya pada klausa where ?

Sekarang bandingkan dengan pra-RFC yang ditautkan di atas. Ini akan memperkenalkan _tag_ (dalam arti sistem tipe) langsung ke posisi argumen fungsi, yaitu pengikatan variabel untuk badan fungsi.

Dalam istilah lain: ini sangat membingungkan dan, seperti yang dinyatakan beberapa orang untuk Sifat impl dalam posisi arg, itu tidak diperlukan. Tolong jangan membuat kesalahan yang sama dua kali untuk menambahkan fitur yang tidak sepadan. Terutama jika Anda "berpura-pura bahwa mereka sedang bertengkar". Karat akan menuju ke arah yang salah di sini. Sintaksnya harus lebih ringan, ya, tetapi tidak lebih membingungkan. Tolong hentikan.

@phaazon
Saya pribadi sepenuhnya setuju dengan proposal tertaut yang memungkinkan konstanta dalam posisi argumen akan menjadi dorongan ergonomis yang sangat baik. Ini bukan hanya tentang tanda tangan fungsi (bisa dibilang klausa where tidak "menyelesaikan" masalah sama sekali, tetapi akan membuat lebih sulit untuk memahami tanda tangan, karena Anda harus mengurai 3 tempat alih-alih hanya satu), tetapi juga tentang bagaimana fungsi-fungsi itu akan digunakan. Membandingkan:

let r = _mm_blend_ps::<{2 * C}>(a, b);
let r = _mm_blend_ps(a, b, 2 * C);

Baris kedua jauh lebih alami menurut saya daripada yang kedua. Contoh lain adalah pembuatan matriks: MatrixF32::new(3, 3) vs. MatrixF32::new::<3, 3>() . Saya sangat tidak setuju bahwa fungsi ini akan "sangat membingungkan". Untuk pengguna itu tidak lebih dari persyaratan tambahan untuk argumen fungsi. Kami sudah dapat meniru argumen const melalui makro (lihat intrinsik SIMD di dalam std ), tetapi cukup jelek dan tidak efisien.

Mengenai impl Trait dalam posisi argumen, awalnya saya juga menentang fitur ini, tetapi setelah beberapa waktu saya berubah pikiran dan saya percaya itu adalah keputusan yang baik pada akhirnya. Satu-satunya bagian yang tidak lengkap saat ini adalah interaksi dengan parameter tipe eksplisit yang disediakan melalui turbofish. Saya pikir solusi yang baik adalah membuat argumen impl Trait tidak terlihat untuk turbofish, yaitu pengguna harus menggunakannya ketika mereka yakin bahwa disambiguasi tipe eksplisit tidak diperlukan. Ini akan memungkinkan kami untuk secara signifikan mengurangi kebutuhan _ di dalam turbofish dalam beberapa skenario.

Saya kira diskusi tentang argumen const di luar topik di sini, jadi mungkin sebaiknya tidak melanjutkannya di sini.

Satu hal yang memisahkan argumen const dari argumen impl Trait (yang disebutkan oleh @PvdBerg1998 ) adalah bahwa argumen tersebut adalah argumen, dengan semua yang menyiratkan inferensi tipe.

Anda tidak dapat meneruskan tipe itu sendiri sebagai argumen, tetapi Anda dapat memberikan nilai, dan membiarkan suatu fungsi menyimpulkan argumen generik const-nya dari argumen kurung normalnya sepenuhnya masuk akal.

Ini cukup umum di C++, misalnya:

template <typename T, size_t N>
size_t length(T (&array)[N]) {
    return N;
}

Hal ini agak berbeda dari hanya menerima parameter const dan desugaring untuk parameter generik const ( fn foo(const N: usize) -> fn foo<const N: usize>() ). Ini lebih dekat dengan membatasi parameter dengan parameter generik const, dan kemudian menyimpulkan argumen generik const itu dari argumen. Jadi contoh matriks di atas mungkin terlihat seperti ini:

impl<const W: usize, const H: usize> MatrixF32<W, H> {
    fn new(w: usize<W>, h: usize<H>) -> Self { .. }
}

...di mana usize<X> adalah sintaks yang dibuat untuk "a usize dengan nilai X ." (Secara analog, Anda mungkin ingin usize<X..Y> untuk tipe rentang, yang telah dibahas dalam konteks generalisasi NonZero .)

Jika Anda ingin menghindari roping dalam tipe jarak jauh, Anda mungkin melihat bahasa tipe dependen yang memungkinkan parameter digunakan secara langsung dalam tipe, dengan sedikit penyesuaian ruang lingkup:

fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

Mereka bukan argumen. Mereka adalah obat generik . Pada level sistem tipe, itu berarti _values_ mereka hidup pada waktu kompilasi, sedangkan nilai argumen fungsi hidup saat runtime. Mencampur keduanya sangat menyesatkan.

Karena saya peduli dengan sistem tipe, proposal ini bertentangan dengan banyak prinsip yang waras dan sehat. Anda tidak ingin mencampur nilai dan tipe. Karena, ya, N (perhatikan saya tidak mengatakan usize , saya mengatakan N ), meskipun Anda pikir itu nilai, jauh lebih mirip dengan tipe daripada sebuah nilai. Sebagai alasan, Haskell tidak memiliki const generik tetapi memiliki DataKinds , memungkinkan untuk mengangkat konstruktor data reguler sebagai tipe:

foo :: Integer -> Integer
foo 0 -- 0 has type Integer

-- but
data P (a :: Integer)

type MyP = P 10 -- 10 has kind Integer, which “value” is the 10 type

Jadi:

fn foo<const X: usize>()

Di sini, X lebih mirip dengan tipe daripada nilai.

Juga, saya peduli tentang monomorfisasi. Jika saya membaca ini:

let x = foo(12, "Hello, world!", None);

Dengan proposal Anda, jika kita melihat definisi dari foo , sulit untuk mengetahui argumen mana yang akan menghasilkan foo . Karena setiap kali Anda memberikan nilai berbeda untuk generik const, Anda membuat fungsi baru yang lengkap.

Mungkin terasa lebih intuitif untuk _Anda_ dan _alasan Anda_, tetapi saya juga punya alasan untuk mengatakan itu sama sekali tidak intuitif dalam hal ketepatan jenis. Menyatakan bahwa mereka adalah argumen seperti menyatakan bahwa fungsi berparameter, dalam matematika, memiliki argumen parameternya. Anda membingungkan parameter dan argumen, menunjukkan betapa buruknya ide itu.

Anda mungkin salah membaca saya, saya secara khusus mengatakan " const argumen ... adalah argumen ," bukan " const generik." Contoh Rust pertama saya juga sama persis dengan DataKinds ( usize<N> adalah versi tingkat tipe N ). Tidak ada masalah di sini dalam hal kebenaran tipe - saya tidak berdebat dalam hal intuisi tetapi dengan analogi sistem tipe yang ada, mapan, dan suara.

Adapun kekhawatiran Anda tentang monomorfisasi- itu tidak berbeda dari hari ini! Setiap argumen berpotensi menyebabkan monomorfisasi baru. Saya juga akan menambahkan bahwa solusi untuk masalah ini adalah dengan mengurangi biaya semua obat generik dengan membagikan kode yang tidak bergantung antara instans, termasuk-dan-terutama dengan mengonversi const generik ke nilai runtime jika memungkinkan.

(Dan saya benar-benar bingung dengan klaim Anda bahwa saya membingungkan parameter dan argumen, saya berhati-hati untuk membedakannya.)

Aku mengerti maksudmu tapi aku masih merasa sangat khawatir. Karena _argument_ ditandai _const_, itu berarti Anda tidak dapat meneruskannya _a value_, runtime berbicara. Anda harus memberikan _constant value_, yang akan dipetakan ke _const generic_. Omong-omong, Anda tidak menyebutkannya di sini, tetapi itu hanya aplikasi _tipe tunggal_ jika Anda menganggap 10 sebagai tipe: kemungkinan nilai tunggalnya adalah 10 .

Karena saya menentang sifat impl di posisi arg, saya juga menentang yang itu (dan Anda akan mengerti mengapa kita bisa membandingkan di sini). Beberapa poin:

  1. Saya tidak berpikir itu perlu sementara _const generics_ menambahkan banyak nilai.
  2. Karena mereka mirip dengan tipe, mereka harus berada di tempat tipe berada, bukan di mana variabel berada.
  3. Saya benar-benar ingin tahu mengapa beberapa orang berpikir <…> lebih sulit daripada (…) , terutama mengingat struct vs fn . Saat ini, struct hanya memiliki <…> dan fn memiliki keduanya, karena keduanya dapat mengaktifkan parameter.
  4. Apa yang benar-benar tidak saya sukai dengan impl Trait in arg position adalah kenyataan bahwa lebih sulit untuk melihat fungsi apa yang benar-benar mengatur parameter. Saat Anda membaca foo<A, B> , Anda tahu ada dua parameter yang akan menghasilkan definisi dan implementasi fungsi.

Saya merasakan apa yang ingin Anda lakukan di sini. foo(1, 3) terasa lebih baik untukmu daripada foo<3>(1) , tapi tidak untukku. Alasan untuk ini adalah bahwa foo(1, 3) harus menerima argumen kedua yang berbasis runtime dan proposal yang Anda berikan melarangnya. Saya dapat melihat aplikasi, terutama dengan array, tetapi saat ini saya tidak menyukai tampilannya. Saya akan lebih menyukai solusi yang mengatakan “jika sebuah argumen adalah const , kita dapat menyimpulkan variabel tipe / const generik.

fn foo<const N: usize>(a: usize, b: usize | N);

Itu hanya sintaks imajiner yang saya buat untuk menjelaskan maksud saya: jika Anda memberikan b sebagai nilai const, N = b dan Anda berakhir dengan sintaks Nice Anda:

foo(1, 3)

Jika b bukan nilai const, Anda harus meneruskan N secara eksplisit:

foo<3>(1, x)

Juga, apa sintaks untuk ini, dengan proposal Anda:

fn foo<const N: usize>(x: [f32; N]);
fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

Ini membawa kita ke pertanyaan tentang item terkait, singkatnya new() adalah fungsi terkait, dan harus dikaitkan dengan tipe konkret, const item generik memiliki semantik seperti "Tipe ini memiliki param ini...", yaitu tipe dependen. Jadi, new() dalam konteks seharusnya tidak memiliki argumen. Penggunaan eksplisit akan terlihat seperti Type<32>::new() . Dan tentu saja semua argumen const generics harus dapat dijalankan.

Saya merasakan apa yang ingin Anda lakukan di sini. foo(1, 3) terasa lebih baik untukmu daripada foo<3>(1), tapi tidak untukku. Alasan untuk ini adalah bahwa foo(1, 3) harus menerima argumen kedua sebagai berbasis runtime dan proposal yang Anda berikan melarangnya.

Itu bukan motivasi atau niat saya - saya tidak berbicara tentang apa yang "terasa lebih baik" dan saya tidak percaya perbedaan kompilasi/run-time perlu digabungkan dengan perbedaan tipe/tingkat nilai. Keduanya sudah terpisah- item const (nilai atau fn) jelas bukan tipe!

Yang saya maksudkan hanyalah koneksi semantik yang ditarik oleh inferensi tipe antara parameter tingkat nilai dan parameter tingkat tipe. Tidak ada hubungannya dengan impl Trait - maksud saya, sekali lagi, adalah untuk membedakan argumen const dari impl Trait , tepatnya untuk menghindari kecemasan yang tidak produktif ini.

Juga, apa sintaks untuk ini, dengan proposal Anda:

fn foo<const N: usize>(x: [f32; N]);

Sintaks itu tidak akan berubah! Saya tidak mengusulkan kami menghapus parameter const dari level tipe (secara sintaksis atau semantik), hanya saja kami menambahkannya ke level nilai, sebagian untuk membantu inferensi dari level tipe.

Di cppcon ada pembicaraan tentang parameter fungsi constexpr, yang tampaknya mirip dengan apa yang dibicarakan @PvdBerg1998 dan sepertinya seperti utas internal yang ditautkan oleh @mark-im. Tampaknya diterima dengan baik di sana.

Sepertinya ide yang bagus, tetapi itu harus diselesaikan setelah kami menyelesaikan implementasi awal untuk const-generics

CppCon 2019: David Stone - Menghapus Metaprogramming Dari C++, Bagian 1 dari N: Constexpr Function Params

Sunting: Ini disebutkan di utas itu, berikut adalah makalah terkait untuk parameter fungsi constexpr
https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md

Saya ingin meminta agar kami mencadangkan diskusi tentang perubahan sintaksis yang begitu signifikan baik untuk utas di internal atau masalah (atau mengubah PR) pada repo RFC. Bagian komentar masalah ini sudah cukup panjang dan sintaks yang dimaksud tampaknya tidak diperlukan untuk MVP const generik yang dapat digunakan.

Jika kami dapat memindahkan komentar, kami mungkin akan melakukannya. Kami mungkin ingin mengunci masalah ke anggota tim @rust-lang dan menyembunyikan semua komentar di luar topik?

Semua diskusi desain harus dilakukan pada repo RFC dan semua laporan bug harus dalam masalah terpisah.

cc @rust-lang/moderation Apakah ada preseden untuk melakukan ini?

Anda dapat _mengubah_ komentar menjadi masalah, tetapi tidak dapat memindahkan komentar. Mengunci baik-baik saja, dan saya hanya menyembunyikan komentar topik.

Dengan dimulainya tahun baru, saya pikir akan lebih baik untuk memberikan pembaruan lain di tempat kita sekarang dengan const generics. Sudah lebih dari 2 tahun sejak const generik RFC diterima. Pada tahun pertama (kira-kira 2018), serangkaian upaya refactoring besar dilakukan untuk memfasilitasi const generics dengan meningkatkan penanganan parameter generik secara umum di seluruh compiler. Pada tahun kedua (kira-kira 2019), pekerjaan mulai mengimplementasikan const generics itu sendiri: pertama dengan mendapatkan kerja minimum, dan kemudian dengan memperlambat peningkatan integritas fitur. Orang-orang juga mulai bereksperimen dengan menggunakan const generik di kompiler itu sendiri. Deskripsi lebih rinci dari upaya ini ada di dua posting pembaruan pertama: [1] , [2] .


Ada kemajuan yang baik dalam beberapa bulan terakhir sejak pembaruan terakhir.

  • @kurus121 :

    • Memperbaiki masalah pemecahan const generik di bawah kompilasi tambahan dan di seluruh peti (https://github.com/rust-lang/rust/pull/65652)

    • Memperbaiki bug dengan inferensi tipe yang memengaruhi generik const dalam diagnostik (https://github.com/rust-lang/rust/pull/65579)

    • Memfaktorkan ulang antarmuka untuk evaluasi const (https://github.com/rust-lang/rust/pull/66877)

  • @yodaldevoid mengimplementasikan disambiguasi tipe dan parameter const sehingga parameter const tidak perlu lagi dibungkus {} (https://github.com/rust-lang/rust/pull/66104)
  • @eddyb memperbaiki penggunaan generik const sebagai panjang array, setelah normalisasi malas diterapkan (https://github.com/rust-lang/rust/pull/66883)
  • @varkor :

    • Menerapkan pemeriksaan structural_match untuk melarang jenis dengan pemeriksaan kesetaraan khusus agar tidak digunakan sebagai generik const (https://github.com/rust-lang/rust/pull/65627)

    • Memperbaiki beberapa diagnostik untuk mempertimbangkan obat generik const (https://github.com/rust-lang/rust/pull/65614)

    • Melakukan berbagai refactoring dan memperbaiki inferensi jenis (https://github.com/rust-lang/rust/pull/65643, https://github.com/rust-lang/rust/pull/65660, https://github. com/rust-lang/rust/pull/65696)

Terima kasih banyak kepada semua orang yang telah membantu dengan obat generik const!


Apa berikutnya? Pemblokir terbesar untuk const generik saat ini adalah lazy normalization , yang diperlukan untuk jenis batasan generik const tertentu (seperti dalam arrays ). @ skinny121 saat ini

Selain normalisasi yang malas, masih ada sejumlah besar bug dan potongan kertas yang ingin kami atasi. Jika Anda tertarik untuk mengatasi salah satu masalah yang tersisa, silakan ping saya tentang masalah tersebut, atau di Discord atau Zulip untuk petunjuk tentang memulai. Saya yakin kita dapat membuat kemajuan yang baik pada tahun 2020 dan mudah-mudahan mendekati titik di mana stabilisasi menjadi diskusi yang layak!

Apakah ada subset generik const yang tidak mencapai normalisasi malas dan tidak memiliki banyak bug dan potongan kertas, tetapi yang dapat mengekspresikan sejumlah besar kode yang berguna? Saya perhatikan bahwa impls std dari banyak sifat untuk array tampaknya berfungsi dengan baik. Mungkin ada penyempitan yang memungkinkan peti lain untuk menulis jenis impls yang kita miliki di std untuk sifat mereka sendiri, meskipun mereka tidak mendukung semua fungsi yang lebih menarik?

Kita sekarang sudah lebih dari setengah tahun, jadi saya akan meringkas secara singkat pekerjaan yang telah berlangsung. Ada banyak orang yang terlibat dalam meningkatkan dukungan const generics dalam 6 bulan terakhir, jadi terima kasih kepada semua orang yang telah membantu dalam beberapa hal! Saya terutama ingin berterima kasih kepada @skinny121 dan @lcnr , yang telah menangani beberapa fitur besar yang const generics kurang untuk sementara waktu: normalisasi malas untuk konstanta dan dukungan argumen const dalam pemanggilan metode , serta memperbaiki banyak bug yang sulit. Upaya mereka terlihat dari ringkasan di bawah ini.

Const generics sekarang digunakan di beberapa tempat di seluruh compiler, dan sudah ada eksperimen dengan sifat dan fungsi yang menggunakan const generics, misalnya slice::array_chunks dan IntoIterator dan FromIterator untuk [T; N] (https://github.com/rust-lang/rust/pull/65819, https://github.com/rust-lang/rust/pull/69985). Pembatasan panjang 32 untuk implementasi sifat larik juga akhirnya disiapkan untuk dihapus .

Saat ini kami sedang mendiskusikan penstabilan subset generik const yang harus mencakup berbagai kasus penggunaan (walaupun masih ada beberapa masalah yang harus diatasi sebelum const generik dapat sepenuhnya distabilkan).


  • @kurus121 :

    • Menerapkan versi pertama normalisasi malas (https://github.com/rust-lang/rust/pull/68505, https://github.com/rust-lang/rust/pull/69181, https://github. com/rust-lang/rust/pull/67717, https://github.com/rust-lang/rust/pull/67890)

    • Peningkatan kinerja (https://github.com/rust-lang/rust/pull/68118)

    • Memperbaiki berbagai kesalahan (https://github.com/rust-lang/rust/pull/68143)

  • @lcnr :

    • Menambahkan dukungan untuk generik const eksplisit dalam argumen tipe untuk metode (https://github.com/rust-lang/rust/pull/74113)

    • Menyelesaikan implementasi pertama normalisasi malas untuk konstanta (https://github.com/rust-lang/rust/pull/71973) dengan @ skinny121

    • Menambahkan serat untuk unused_braces (https://github.com/rust-lang/rust/pull/70081)

    • Memperbaiki masalah dengan const generik dalam pola (https://github.com/rust-lang/rust/pull/70562)

    • Melarang dyn Trait untuk const parameter generik (https://github.com/rust-lang/rust/pull/71038)

    • Peningkatan pencetakan cantik (https://github.com/rust-lang/rust/pull/72052)

    • Memperbaiki berbagai kesalahan (https://github.com/rust-lang/rust/pull/70032, https://github.com/rust-lang/rust/pull/70107, https://github.com/rust- lang/rust/pull/70223, https://github.com/rust-lang/rust/pull/70249, https://github.com/rust-lang/rust/pull/70284, https://github. com/rust-lang/rust/pull/70319, https://github.com/rust-lang/rust/pull/70541, https://github.com/rust-lang/rust/pull/72066, https: //github.com/rust-lang/rust/pull/74159

    • Pemfaktoran ulang (https://github.com/rust-lang/rust/pull/70478)

    • Tes tambahan (https://github.com/rust-lang/rust/pull/74392, https://github.com/rust-lang/rust/pull/74445)

  • @eddyb :

    • Memperbaiki masalah dengan const generik dalam panjang array (https://github.com/rust-lang/rust/pull/70452)

    • Peningkatan pencetakan cantik (https://github.com/rust-lang/rust/pull/71232)

    • Penanganan kesalahan yang ditingkatkan (https://github.com/rust-lang/rust/pull/71049)

    • Pemfaktoran ulang (https://github.com/rust-lang/rust/pull/70164)

  • @ Aaron1011 : kesalahan tetap dengan penggunaan generik const lintas peti (https://github.com/rust-lang/rust/pull/72600)
  • @petrochenkov : memperbaiki masalah dengan resolusi nama dan const generik (https://github.com/rust-lang/rust/pull/70006)
  • @yodaldevoid : memperbaiki masalah dengan penggunaan seumur hidup dalam parameter generik const (https://github.com/rust-lang/rust/pull/74051)
  • @contrun : memperbaiki ICE (https://github.com/rust-lang/rust/pull/69859)
  • @oli-obk: refactoring (https://github.com/rust-lang/rust/pull/69981)
  • @Centril : diagnostik yang ditingkatkan (https://github.com/rust-lang/rust/pull/70261)
  • @DutchGhost : menambahkan tes (https://github.com/rust-lang/rust/pull/70539)
  • @varkor :

    • Peningkatan pencetakan cantik (https://github.com/rust-lang/rust/pull/67909, https://github.com/rust-lang/rust/pull/68111)

    • Memperbaiki berbagai kesalahan (https://github.com/rust-lang/rust/pull/67906, https://github.com/rust-lang/rust/pull/68388, https://github.com/rust- lang/karat/tarik/68434)

    • Diagnostik yang ditingkatkan (https://github.com/rust-lang/rust/pull/70845)

    • Tes tambahan (https://github.com/rust-lang/rust/pull/68312, https://github.com/rust-lang/rust/pull/70939)


Sementara const generics telah berkembang jauh, masih ada bug dan ujung yang tajam . Jika Anda tertarik untuk mengatasi salah satu masalah yang tersisa, silakan ping saya, @lcnr atau @eddyb tentang masalah tersebut, atau di Zulip, untuk petunjuk tentang memulai. Terima kasih!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat