Rust: masalah pelacakan const fn (RFC 911)

Dibuat pada 6 Apr 2015  ·  274Komentar  ·  Sumber: rust-lang/rust

https://github.com/rust-lang/rust/issues/57563 | masalah pelacakan meta baru

konten lama

Masalah pelacakan untuk rust-lang/rfcs#911.

Masalah ini telah ditutup demi masalah yang lebih bertarget:

Hal-hal yang harus dilakukan sebelum stabilisasi:

CTFE = https://en.wikipedia.org/wiki/Compile_time_function_execution

A-const-eval A-const-fn B-RFC-approved B-RFC-implemented B-unstable C-tracking-issue T-lang

Komentar yang paling membantu

Tolong abaikan ini jika saya benar-benar tidak mengerti.

Masalah yang saya lihat dengan RFC ini adalah bahwa sebagai pengguna, Anda harus menandai fungsi const fn sebanyak mungkin karena itu mungkin akan menjadi praktik terbaik. Hal yang sama terjadi saat ini di C++ dengan contexpr. Saya pikir ini hanya verbositas yang tidak perlu.

D tidak memiliki const fn tetapi memungkinkan fungsi apa pun dipanggil pada waktu kompilasi (dengan beberapa pengecualian).

Misalnya

// Standalone example.
struct Point { x: i32, y: i32 }

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x: x, y: y }
    }

    fn add(self, other: Point) -> Point {
        Point::new(self.x + other.x, self.y + other.y)
    }
}

const ORIGIN: Point = Point::new(0, 0); // works because 0, 0 are both known at compile time 
const ORIGIN2: Point = Point::new(0, 0); // ditto

const ANOTHER: Point = ORIGIN.add(ORIGIN2); // works because ORIGIN and ORIGIN2 are both const.
{
    let x: i32 = 42;
    let y: i32 = 24;
    const SOME_POINT: Point = Point::new(x, y); // Error: x and y are not known at compile time
}
{
    const x: i32 = 42;
    const y: i32 = 24;
    const SOME_POINT: Point = Point::new(x, y); // Works x and y are both known at compile time.
}

Catatan, saya bukan pengguna Rust dan saya baru membaca RFC beberapa menit yang lalu, jadi ada kemungkinan saya salah memahami sesuatu.

Semua 274 komentar

Apakah ini ditutup oleh #25609?

@Munksgaard Itu hanya menambahkan dukungan ke kompiler AFAIK. Ada banyak fungsi di stdlib yang perlu diubah menjadi const fn dan diuji untuk kerusakan. Saya tidak tahu apa kemajuan dalam hal itu.

Saya berharap ini diterapkan pada std::ptr::null() dan null_mut() sehingga kita dapat menggunakannya untuk menginisialisasi static mut *MyTypeWithDrop tanpa menggunakan 0usize as *mut _

EDIT: Dihapus karena keluar dari subjek

Untuk lebih jelasnya, pertanyaan di sini bukan terutama tentang kegunaan fitur tersebut, melainkan tentang cara terbaik untuk merumuskannya (atau kerangka kerja terbaik untuk merumuskannya). Lihat diskusi RFC.

Ini sekarang masalah pelacakan untuk stabilisasi akhirnya.

https://github.com/rust-lang/rust/issues/29107 telah ditutup.

Saya tidak setuju bahwa "Integrasi dengan pola", atau perubahan apa pun pada perpustakaan standar harus memblokir ini. Ini sangat berguna bahkan tanpa perubahan itu, dan perubahan itu dapat dilakukan nanti. Secara khusus, saya ingin segera mulai menggunakan const fn dalam kode saya sendiri.

Dengan demikian, dapatkah status stabilisasi ini dievaluasi kembali?

Saya tidak ragu bahwa const fn bahkan dalam bentuknya yang terbatas saat ini akan menjadi fungsionalitas yang berguna untuk dimiliki, tetapi apa yang benar-benar saya inginkan, idealnya sebelum melangkah lebih jauh di sepanjang jalan ini, adalah bagi mereka yang mendukung " const fn pendekatan" untuk memikirkan dan mengartikulasikan permainan akhir pilihan mereka. Jika kita terus menambahkan fungsionalitas yang tampak berguna secara bertahap dengan cara yang paling jelas, tampaknya sangat mungkin bagi saya bahwa pada akhirnya kita akan menyalin kurang lebih keseluruhan desain constexpr C++. Apakah itu sesuatu yang membuat kita nyaman? Bahkan jika kita mengatakan ya, saya lebih suka kita memilih jalan itu dengan cara yang jernih, daripada mundur ke dalamnya dengan langkah-langkah kecil dari waktu ke waktu, sebagai jalan yang paling sedikit perlawanannya, sampai itu menjadi tak terelakkan.

(Mengingat bahwa semantik kode Rust yang aman harus sepenuhnya dapat ditentukan, tampaknya pada akhirnya setidaknya setiap fungsi yang tidak (transitif) bergantung pada unsafe harus dapat ditandai sebagai const . Dan mengingat unsafe seharusnya menjadi detail implementasi, saya yakin orang akan mendorong untuk entah bagaimana melonggarkan pembatasan itu juga. Saya lebih suka kita melihat ke luar negeri dan mencoba menemukan yang lebih kohesif, mampu, dan cerita yang terintegrasi dengan baik untuk pementasan dan perhitungan tingkat tipe.)

@glaebhoerl

Saya tidak ragu bahwa const fn bahkan dalam bentuknya yang terbatas saat ini akan menjadi fungsionalitas yang berguna untuk dimiliki, tetapi apa yang benar-benar saya sukai, idealnya sebelum melangkah lebih jauh di sepanjang jalan ini, adalah bagi mereka yang mendukung "pendekatan const fn" untuk memikirkan dan mengartikulasikan permainan akhir pilihan mereka... tampaknya sangat mungkin bagi saya bahwa kita pada akhirnya akan menyalin kurang lebih keseluruhan desain constexpr C++.

Apa yang saya pribadi ingin, bahkan lebih dari itu, adalah bahwa kita memiliki pandangan yang cukup jelas tentang bagaimana kita akan mengimplementasikannya, dan bagian bahasa apa yang akan kita bahas. Yang mengatakan, ini sangat erat kaitannya dengan dukungan untuk konstanta terkait atau generik di atas bilangan bulat dalam pikiran saya.

@eddyb dan saya baru-baru ini membuat sketsa pada skema yang dapat memungkinkan evaluasi konstan dari petak kode yang sangat luas. Pada dasarnya menurunkan semua konstanta ke MIR dan menafsirkannya (dalam beberapa kasus,
interpretasi abstrak, jika ada obat generik yang belum dapat Anda evaluasi, di situlah hal-hal menjadi paling menarik bagi saya).

Namun, meskipun tampaknya cukup mudah untuk mendukung sebagian besar "bahasa bawaan", kode nyata dalam praktiknya bertentangan dengan kebutuhan untuk melakukan alokasi memori dengan sangat cepat. Dengan kata lain, Anda ingin menggunakan Vec atau wadah lain. Dan di situlah seluruh skema penafsiran ini mulai menjadi lebih rumit dalam pikiran saya.

Yang mengatakan, @glaebhoerl , saya juga ingin mendengar Anda mengartikulasikan akhir permainan alternatif pilihan Anda . Saya pikir Anda membuat sketsa beberapa pemikiran seperti itu di const fn RFC, tetapi saya pikir akan lebih baik untuk mendengarnya lagi, dan dalam konteks ini. :)

Masalah dengan alokasi membuatnya melarikan diri ke run-time.
Jika kita entah bagaimana dapat melarang melintasi penghalang waktu kompilasi/waktu berjalan itu, maka saya yakin kita dapat memiliki liballoc yang berfungsi dengan const fn .
Tidak akan lebih sulit untuk mengelola alokasi semacam itu daripada berurusan dengan nilai-nilai yang dapat dialamatkan byte pada tumpukan yang ditafsirkan.

Atau, kita dapat membuat kode runtime untuk mengalokasikan dan mengisi nilai setiap kali penghalang itu harus dilewati, meskipun saya tidak yakin jenis kasus penggunaan apa yang dimiliki.

Ingatlah bahwa bahkan dengan evaluasi penuh constexpr -seperti, const fn _masih_ murni: menjalankannya dua kali pada data 'static akan menghasilkan hasil yang sama persis dan tidak efek samping.

@nikomatsakis Jika saya punya, saya akan menyebutkannya. :) Saya terutama hanya melihat yang tidak diketahui. Semuanya dengan const s sebagai bagian dari sistem generik tentu saja merupakan bagian dari apa yang saya pahami sebagai desain C++. Sejauh memiliki parameter generik const s dan const yang terkait, mengingat kami telah memiliki array ukuran tetap dengan const s sebagai bagian dari jenisnya dan ingin mengabstraksikannya mereka, saya akan terkejut jika ada cara yang jauh lebih baik -- dibandingkan hanya lebih umum -- untuk melakukannya. Bagian const fn terasa lebih dapat dipisahkan dan bervariasi. Sangat mudah untuk membayangkan mengambil sesuatu lebih jauh dan memiliki hal-hal seperti const impl s dan const Trait dalam obat generik, tapi saya _sure_ ada prior art untuk hal umum semacam ini yang telah menemukan jawabannya dan kita harus berusaha menemukannya.

Dari kasus penggunaan utama untuk bahasa Rust, yang terutama membutuhkan kontrol tingkat rendah, seperti kernel, tampaknya sudah terlayani dengan baik, tetapi area lain di mana Rust dapat memiliki banyak potensi adalah hal-hal yang terutama membutuhkan kinerja tinggi, dan dalam dukungan ruang yang kuat (dalam beberapa bentuk) untuk perhitungan bertahap (yang const fn sudah menjadi contoh yang sangat terbatas) sepertinya itu bisa menjadi pengubah permainan. (Baru dalam beberapa minggu terakhir saya menemukan dua tweet terpisah oleh orang-orang yang memutuskan untuk beralih dari Rust ke bahasa dengan kemampuan pementasan yang lebih baik.) Saya tidak yakin apakah ada solusi yang ada dalam bahasa "dekat dengan kita" -- constexpr C++, CTFE ad-hoc D, makro prosedural kami -- benar-benar menginspirasi dan cukup kuat/lengkap untuk hal semacam ini. (Makro prosedural tampak seperti hal yang baik untuk dimiliki, tetapi lebih untuk abstraksi dan DSL, tidak sebanyak untuk pembuatan kode berorientasi kinerja.)

Adapun apa yang _akan_ menginspirasi dan cukup baik... Saya belum melihatnya, dan saya tidak cukup akrab dengan seluruh ruang untuk mengetahui, tepatnya, di mana mencarinya. Tentu saja, berdasarkan hal di atas, kita mungkin ingin setidaknya melirik Julia dan Terra, meskipun mereka tampak seperti bahasa yang sangat berbeda dari Rust dalam banyak hal. Saya tahu Oleg Kiselyov telah melakukan banyak pekerjaan menarik di bidang ini. Karya Tiark Rompf tentang Lancet dan Pementasan Modular Ringan untuk Scala tampaknya layak untuk dilihat. Saya ingat pernah melihat presentasi oleh @kmcallister di beberapa titik tentang seperti apa Rust yang diketik secara dependen (yang mungkin setidaknya lebih umum daripada menempel const di mana-mana), dan saya juga ingat melihat sesuatu dari Oleg ke efek tipe itu sendiri adalah bentuk pementasan (yang terasa alami mengingat pemisahan fase antara kompilasi dan runtime sangat mirip dengan tahapan)... banyak koneksi potensial yang menarik di berbagai arah, itulah sebabnya rasanya seperti terlewatkan kesempatan jika kita hanya berkomitmen pada solusi pertama yang terjadi pada kita. :)

(Ini hanya braindump dan saya hampir pasti telah mengkarakterisasi banyak hal dengan tidak sempurna.)

Namun, meskipun tampaknya cukup mudah untuk mendukung sebagian besar "bahasa bawaan", kode nyata dalam praktiknya bertentangan dengan kebutuhan untuk melakukan alokasi memori dengan sangat cepat. Dengan kata lain, Anda ingin menggunakan Vec atau wadah lain. Dan di situlah seluruh skema penafsiran ini mulai menjadi lebih rumit dalam pikiran saya.

Saya tidak setuju dengan karakterisasi "kode nyata dalam praktik". Saya pikir ada minat besar pada Rust karena membantu mengurangi kebutuhan alokasi memori heap. Kode saya, khususnya, melakukan upaya nyata untuk menghindari alokasi tumpukan bila memungkinkan.

Mampu melakukan lebih dari itu akan menjadi _nice_ tetapi mampu membuat instance statis dari tipe non-sepele dengan invarian yang didukung kompiler sangat penting. Pendekatan C++ constexpr sangat membatasi, tetapi ini lebih dari yang saya butuhkan untuk kasus penggunaan saya: Saya perlu menyediakan fungsi yang dapat membuat instance tipe T dengan parameter x , y , dan z sedemikian rupa sehingga fungsi tersebut menjamin bahwa x , y , dan z valid (mis. x < y && z > 0 ), sehingga hasilnya dapat berupa variabel static , tanpa menggunakan kode inisialisasi yang dijalankan saat startup.

@briansmith FWIW pendekatan lain yang memiliki peluang untuk menyelesaikan kasus penggunaan yang sama adalah jika makro memiliki privacy hygiene , yang saya yakini (harap) kami rencanakan untuk membuatnya.

@briansmith FWIW pendekatan lain yang memiliki peluang untuk memecahkan kasus penggunaan yang sama adalah jika makro memiliki kebersihan privasi, yang saya yakini (harap) kami rencanakan untuk mereka miliki.

Saya kira jika Anda menggunakan makro prosedural maka Anda dapat mengevaluasi x < y && z > 0 pada waktu kompilasi. Tapi, sepertinya butuh waktu berbulan-bulan sebelum makro prosedural dapat digunakan di Rust yang stabil, jika memang ada. const fn menarik karena dapat diaktifkan untuk stabil Rust _now_, sejauh yang saya mengerti keadaannya.

@glaebhoerl Saya tidak akan menahan napas untuk kebersihan yang ketat, sangat mungkin kita akan memiliki mekanisme pelarian (seperti LISP nyata), jadi Anda mungkin tidak menginginkannya untuk tujuan keamanan apa pun.

@glaebhoerl ada juga https://anydsl.github.io/, yang bahkan menggunakan
Sintaks seperti karat ;) mereka pada dasarnya menargetkan komputasi bertahap dan dalam
evaluasi parsial tertentu.

Pada Sabtu, 16 Januari 2016 pukul 18:29, Eduard-Mihai Burtescu <
[email protected]> menulis:

@glaebhoerl https://github.com/glaebhoerl Saya tidak akan menahan napas untuk
kebersihan yang ketat, sangat mungkin kita akan memiliki mekanisme pelarian
(seperti LISP asli), jadi Anda mungkin tidak menginginkannya untuk keamanan apa pun
tujuan.


Balas email ini secara langsung atau lihat di GitHub
https://github.com/rust-lang/rust/issues/24111#issuecomment -172271960.

29525 harus diperbaiki sebelum stabilisasi

Mengingat bahwa semantik kode Rust yang aman harus dapat didefinisikan sepenuhnya, tampaknya pada akhirnya setidaknya setiap fungsi yang tidak (transitif) bergantung pada unsafe harus dapat ditandai sebagai const . Dan mengingat unsafe seharusnya menjadi detail implementasi, saya yakin orang akan mendorong untuk melonggarkan batasan itu juga.

Hanya sebuah pemikiran: jika kita pernah secara formal mendefinisikan model memori Rust , maka bahkan kode unsafe dapat berpotensi dievaluasi dengan aman dan masuk akal pada waktu kompilasi dengan menafsirkannya secara abstrak/simbolis -- yaitu, penggunaan pointer mentah tidak akan' t berubah menjadi akses memori langsung seperti saat runtime, melainkan sesuatu (sebagai contoh untuk ilustrasi) seperti pencarian ke dalam peta hash dari alamat yang dialokasikan, bersama dengan jenis dan nilainya, atau serupa, dengan setiap langkah diperiksa validitasnya -- jadi bahwa setiap eksekusi yang perilakunya tidak ditentukan akan _strictly_ kesalahan yang dilaporkan oleh kompiler, alih-alih kerentanan keamanan di rustc . (Ini mungkin juga terkait dengan situasi yang menangani isize dan usize pada waktu kompilasi secara simbolis atau dengan cara yang bergantung pada platform.)

Saya tidak yakin di mana itu meninggalkan kita sehubungan dengan const fn . Di satu sisi, itu kemungkinan akan membuka kode yang jauh lebih berguna untuk tersedia pada waktu kompilasi -- Box , Vec , Rc , apa pun yang menggunakan unsafe untuk pengoptimalan kinerja -- yang bagus. Satu pembatasan sewenang-wenang lebih sedikit. Di sisi lain, batas untuk "apa yang mungkin menjadi const fn " sekarang pada dasarnya akan dipindahkan ke _apa pun yang tidak melibatkan FFI_. Jadi apa yang _sebenarnya_ akan kami lacak dalam sistem tipe, dengan kedok const ness, adalah apa yang (secara transitif) bergantung pada FFI dan apa yang tidak. Dan apakah sesuatu menggunakan FFI atau tidak adalah _still_ sesuatu yang dianggap orang sebagai detail implementasi internal, dan pembatasan ini (tidak seperti unsafe ) _benar-benar_ tampaknya tidak layak untuk diangkat. Dan di bawah skenario ini Anda mungkin akan memiliki lebih banyak fn yang memenuhi syarat untuk const daripada yang tidak.

Jadi Anda masih memiliki const yang berputar di sekitar pembatasan yang mengekspos implementasi sewenang-wenang, dan Anda juga akhirnya harus menulis const hampir di mana-mana. Kedengarannya juga tidak terlalu menarik...

yaitu, penggunaan pointer mentah tidak akan berubah menjadi akses memori langsung seperti saat runtime, melainkan sesuatu ... seperti pencarian ke hashmap dari alamat yang dialokasikan,

@glaebhoerl Yah, itu cukup banyak model yang saya jelaskan dan yang miri @tsion implementasikan.

Saya pikir perbedaan FFI sangat penting karena kemurnian, yang _diperlukan_ untuk koherensi.
Anda _tidak bisa_ menggunakan GHC untuk Rust const fn s karena memiliki unsafePerformIO .

Saya sendiri tidak terlalu menyukai kata kunci const itulah sebabnya saya setuju dengan const fn foo<T: Trait> daripada const fn foo<T: const Trait> (karena membutuhkan const impl Trait for T ).

Sama seperti Sized , kami mungkin memiliki default yang salah, tetapi saya belum melihat proposal lain yang dapat bekerja secara realistis.

@eddyb Saya pikir Anda bermaksud menautkan ke https://internals.rust-lang.org/t/mir-constant-evaluation/3143/31 (komentar 31, bukan 11).

@tsion Diperbaiki, terima kasih!

Tolong abaikan ini jika saya benar-benar tidak mengerti.

Masalah yang saya lihat dengan RFC ini adalah bahwa sebagai pengguna, Anda harus menandai fungsi const fn sebanyak mungkin karena itu mungkin akan menjadi praktik terbaik. Hal yang sama terjadi saat ini di C++ dengan contexpr. Saya pikir ini hanya verbositas yang tidak perlu.

D tidak memiliki const fn tetapi memungkinkan fungsi apa pun dipanggil pada waktu kompilasi (dengan beberapa pengecualian).

Misalnya

// Standalone example.
struct Point { x: i32, y: i32 }

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x: x, y: y }
    }

    fn add(self, other: Point) -> Point {
        Point::new(self.x + other.x, self.y + other.y)
    }
}

const ORIGIN: Point = Point::new(0, 0); // works because 0, 0 are both known at compile time 
const ORIGIN2: Point = Point::new(0, 0); // ditto

const ANOTHER: Point = ORIGIN.add(ORIGIN2); // works because ORIGIN and ORIGIN2 are both const.
{
    let x: i32 = 42;
    let y: i32 = 24;
    const SOME_POINT: Point = Point::new(x, y); // Error: x and y are not known at compile time
}
{
    const x: i32 = 42;
    const y: i32 = 24;
    const SOME_POINT: Point = Point::new(x, y); // Works x and y are both known at compile time.
}

Catatan, saya bukan pengguna Rust dan saya baru membaca RFC beberapa menit yang lalu, jadi ada kemungkinan saya salah memahami sesuatu.

@MaikKlein ada banyak diskusi tentang CTFE di diskusi RFC

Saya tidak melihat komentar terbaru yang menjelaskan pemblokir di sini, dan operasinya tidak terlalu mencerahkan. Apa statusnya. Bagaimana kita bisa memindahkan ini melintasi garis finish?

Lihat https://github.com/rust-lang/rust/issues/29646#issuecomment -271759986. Kami juga perlu mempertimbangkan kembali posisi kami tentang ketegasan karena miri mendorong batas "efek samping global" ( @solson dan @nikomatsakis baru saja membicarakan hal ini di IRC).

Masalah yang saya lihat dengan RFC ini adalah bahwa sebagai pengguna, Anda harus menandai fungsi const fn sebanyak mungkin karena itu mungkin akan menjadi praktik terbaik.

Meskipun kita dapat membuat fungsi arbitrer dapat dipanggil, jika fungsi tersebut mengakses kode C atau statika, kita tidak akan dapat menghitungnya. Sebagai solusi, saya menyarankan lint yang akan memperingatkan tentang fungsi publik yang dapat berupa const fn.

Saya setuju tentang serat. Ini mirip dengan lint bawaan yang ada missing_docs , missing_debug_implementations , dan missing_copy_implementations .

Namun, ada masalah dengan mengaktifkan lint secara default... itu akan memperingatkan tentang fungsi yang secara eksplisit tidak Anda inginkan const , katakanlah, karena Anda berencana untuk mengubah fungsi tersebut di lain waktu tidak bisa const dan tidak ingin mengkomit antarmuka Anda ke const (menghapus const adalah perubahan yang melanggar).

Saya kira #[allow(missing_const)] fn foo() {} mungkin berfungsi dalam kasus itu?

@eddyb @nikomatsakis Poin "penghapusan const saya adalah perubahan yang melanggar" menunjukkan bahwa bagaimanapun juga, kita ingin memiliki kata kunci, karena janji ke hilir bahwa fn akan _tetap_ const hingga versi utama berikutnya.

Sayang sekali berapa banyak const perlu ditaburkan melalui std dan perpustakaan lain, tetapi saya tidak melihat bagaimana Anda dapat menghindarinya, kecuali itu hanya diperlukan di publik- menghadapi item, dan itu sepertinya aturan yang membingungkan.

kecuali itu hanya diperlukan pada barang-barang yang menghadap publik, dan itu sepertinya aturan yang membingungkan.

Saya suka yang ini... Saya tidak berpikir itu akan membingungkan. Antarmuka publik Anda dilindungi karena Anda tidak dapat membuat fungsi non-const yang dipanggil oleh const fn

Secara teknis akan lebih baik untuk membubuhi keterangan fungsi sebagai notconst , karena saya berharap ada lebih banyak const fn daripada sebaliknya.

notconst juga akan lebih konsisten dengan filosofi desain Rust. (mis. " mut , bukan const ")

kecuali itu hanya diperlukan pada barang-barang yang menghadap publik, dan itu sepertinya aturan yang membingungkan.

Saya suka yang ini... Saya tidak berpikir itu akan membingungkan.

Saya flip flopping pada ide ini. Ini memiliki manfaatnya ( hanya memikirkan const fn saat membuat keputusan antarmuka publik) tetapi saya memikirkan cara lain yang dapat membingungkan:

Antarmuka publik Anda dilindungi karena Anda tidak dapat membuat fungsi non-const yang dipanggil oleh const fn

Ini benar, dan sayangnya itu akan menyiratkan bahwa ketika seorang penulis perpustakaan menandai fungsi publik const , maka mereka secara implisit menandai semua fungsi yang dipanggil secara transitif oleh fungsi itu const juga, dan ada kemungkinan mereka secara tidak sengaja menandai fungsi yang tidak mereka inginkan, sehingga mencegah mereka menulis ulang fungsi internal tersebut menggunakan fitur non-const di masa mendatang.


Saya berharap akan ada lebih banyak const fn daripada sebaliknya.

Saya berpikir seperti ini untuk sementara waktu, tetapi itu hanya akan berlaku untuk peti perpustakaan Rust murni. Tidak mungkin membuat fns const berbasis FFI (walaupun hanya berbasis FFI transitif, yang merupakan banyak hal), jadi jumlah const fn semata-mata mungkin tidak terlalu buruk seperti yang Anda dan saya pikirkan.


Kesimpulan saya saat ini: const fn non-eksplisit tampaknya bermasalah. Mungkin tidak ada cara yang baik untuk menghindari penulisan kata kunci yang banyak.

Juga, sebagai catatan, notconst akan menjadi perubahan besar.

@solson Poin yang sangat bagus.

Ingatlah bahwa kata kunci menjadi lebih berbulu jika Anda mencoba menggunakannya dengan metode sifat. Membatasinya ke definisi sifat tidak cukup berguna dan memberi anotasi impls menghasilkan aturan "const fn parametrim" yang tidak sempurna.

Saya merasa trade-off ini cukup dibahas secara menyeluruh ketika kami mengadopsi const fn di tempat pertama. Saya pikir analisis @solson juga benar. Saya kira satu-satunya hal yang telah berubah adalah bahwa mungkin persentase fns polisi telah tumbuh lebih besar, tetapi saya tidak berpikir cukup untuk mengubah tradeoff mendasar di sini. Akan menjengkelkan untuk secara bertahap harus menambahkan const fn ke antarmuka publik Anda dan sebagainya, tetapi begitulah kehidupan.

@nikomatsakis Yang mengganggu saya adalah kombinasi dari dua fakta ini:

  • kami tidak dapat memeriksa semuanya sebelumnya, kode unsafe dapat "secara dinamis non-const"
  • apa pun yang kita lakukan untuk obat generik dan impls sifat, itu akan menjadi tradeoff antara "benar" dan fleksibel

Mengingat bahwa "efek samping global" adalah hal utama yang mencegah kode menjadi const fn , bukankah ini "sistem efek" yang dulu dimiliki dan dihapus Rust?
Bukankah seharusnya kita berbicara tentang "stabilitas efek"? Tampaknya mirip dengan kode dengan asumsi beberapa perpustakaan tidak pernah membuat panik IMO.

@eddyb benar-benar const adalah sistem efek dan ya itu memang datang dengan semua kerugian yang membuat kami ingin menghindarinya sebanyak mungkin... Masuk akal jika kami akan menanggung rasa sakit menambahkan dalam sistem efek, kita mungkin ingin mempertimbangkan beberapa sintaks yang dapat kita skalakan ke jenis efek lain. Sebagai contoh, kami membayar harga yang sama dengan unsafe (juga sebuah efek), meskipun saya tidak yakin apakah masuk akal untuk memikirkan menyatukannya.

Fakta bahwa pelanggaran dapat terjadi secara dinamis tampaknya menjadi lebih banyak alasan untuk membuat keikutsertaan ini, bukan?

Bagaimana dengan ini:

Secara umum, saya pikir, const fn s hanya boleh digunakan untuk konstruktor ( new ) atau jika benar-benar diperlukan.

Namun, terkadang Anda mungkin ingin menggunakan metode lain untuk membuat konstanta dengan mudah. Saya pikir kita bisa memecahkan masalah ini untuk banyak kasus dengan membuat constness default tetapi hanya untuk modul yang mendefinisikan . Dengan cara ini, tanggungan tidak dapat mengasumsikan constness kecuali dijamin secara eksplisit dengan const , sementara masih memiliki kemudahan untuk membuat konstanta dengan fungsi tanpa membuat semuanya const .

@torkleyy Anda sudah bisa melakukannya dengan memiliki pembantu yang tidak diekspor.

Saya tidak melihat argumen kuat bahwa fungsi pembantu pribadi tidak boleh secara implisit const , jika memungkinkan. Saya pikir @solson mengatakan bahwa membuat const eksplisit, bahkan untuk fungsi pembantu, memaksa programmer untuk berhenti sejenak dan mempertimbangkan apakah mereka ingin berkomitmen pada fungsi itu menjadi const . Tetapi jika programmer sudah diharuskan memikirkannya untuk fungsi publik, bukankah itu cukup? Tidakkah layak untuk tidak menulis const di mana-mana?

Di IRC , @eddyb mengusulkan pemisahan gerbang fitur ini sehingga kami dapat menstabilkan panggilan ke const fns sebelum mengetahui detail deklarasi dan badan mereka. Apakah itu terdengar seperti ide yang bagus?

@durka Kedengarannya bagus bagi saya, sebagai pengguna Rust yang tidak tahu banyak tentang internal kompiler.

Maafkan kurangnya pemahaman saya di sini, tetapi apa artinya menstabilkan panggilan ke fungsi const tanpa menstabilkan deklarasi.

Apakah kita mengatakan bahwa kompiler entah bagaimana akan tahu apa yang konstan dan tidak konstan melalui beberapa cara, tetapi biarkan bagian itu terbuka untuk diskusi/implementasi untuk saat ini?

Lalu bagaimana panggilan dapat distabilkan jika kompiler nantinya berubah pikiran tentang apa yang konstan?

@nixpulvis Beberapa const fn s sudah ada di perpustakaan standar, misalnya UnsafeCell::new . Proposal ini akan memungkinkan untuk memanggil fungsi tersebut dalam konteks konstan, misalnya inisialisasi item static .

@nixpulvis Yang saya maksud adalah panggilan ke fungsi const fn yang ditentukan oleh kode penggunaan tidak stabil (seperti pustaka standar), dari konteks konstan, bukan fungsi reguler yang ditentukan dalam kode Rust stabil.

Sementara saya semua mendukung stabilisasi panggilan ke const fn s terlebih dahulu jika itu bisa terjadi lebih cepat, tidak jelas bagi saya apa yang memblokir stabilisasi semua fitur const fn . Apa kekhawatiran yang tersisa hari ini? Apa yang akan menjadi jalan untuk mengatasinya?

@SimonSapin Lebih dari itu kami tidak jelas desain untuk mendeklarasikan const fn s hari ini skala dengan baik, kami juga tidak yakin pada interaksi antara mereka dan sifat-sifat dan seberapa banyak fleksibilitas yang seharusnya ada.

Saya pikir saya cenderung untuk menstabilkan penggunaan const fn. Ini sepertinya kemenangan yang ergonomis dan ekspresif dan saya masih tidak bisa membayangkan cara yang lebih baik untuk menangani evaluasi konstan waktu kompilasi daripada hanya bisa "menulis kode normal".

menstabilkan penggunaan const fn.

Ini juga menstabilkan beberapa fungsi di perpustakaan standar sebagai const , tim perpustakaan harus melakukan audit setidaknya.

Saya telah mengirimkan PR https://github.com/rust-lang/rust/issues/43017 untuk menstabilkan permintaan, bersama dengan daftar fungsi yang akan diaudit per @petrochenkov.

Saya memiliki pertanyaan/komentar tentang bagaimana ini dapat digunakan dalam situasi sifat/impl tertentu. Secara hipotesis, katakanlah kita memiliki perpustakaan matematika dengan sifat Zero :

pub trait Zero {
    fn zero () -> Self;
}

Sifat ini tidak memerlukan metode zero menjadi const , karena ini akan mencegahnya diterapkan oleh beberapa tipe BigInt yang didukung oleh Vec . Tetapi untuk skalar mesin dan tipe sederhana lainnya, akan jauh lebih praktis jika metodenya adalah const .

impl Zero for i32 {
    const fn zero () -> i32 { 0 } // const
}

impl Zero for BigInt {
    fn zero () -> BigInt { ... } // not const
}

Sifat tidak mengharuskan metode menjadi const , tetapi tetap diperbolehkan, karena const menambahkan batasan pada implementasi dan tidak mengabaikannya. Ini mencegah memiliki versi normal dan versi const dari fungsi yang sama untuk beberapa jenis. Yang saya ingin tahu adalah apakah ini sudah ditangani?

Mengapa Anda ingin implementasi sifat yang berbeda berperilaku berbeda? Anda tidak dapat menggunakannya dalam konteks umum. Anda bisa membuat impl lokal pada skalar dengan const fn.

@Daggerbot Itulah satu-satunya cara saya melihat ke depan untuk const fn dalam sifat - memiliki sifat yang mengharuskan semua impls const fn jauh lebih jarang daripada memiliki " const impl s" yang efektif .

@jethrogb Anda bisa, meskipun membutuhkan constness menjadi properti dari impl.
Apa yang saya pikirkan adalah bahwa const fn generik dengan, misalnya T: Zero terikat, akan memerlukan impl dari Zero untuk T s dipanggil dengan hanya berisi metode const fn , ketika panggilan berasal dari konteks konstan itu sendiri (mis const fn lainnya).

Itu tidak sempurna tetapi tidak ada alternatif superior yang diajukan - IMO yang paling dekat dengan itu adalah "izinkan panggilan dan kesalahan apa pun jauh dari tumpukan panggilan jika sesuatu yang tidak mungkin dilakukan pada waktu kompilasi", yang tidak seburuk itu mungkin tampak pada kesan pertama - sebagian besar kekhawatirannya berkaitan dengan kompatibilitas mundur, yaitu menandai suatu fungsi const fn memastikan bahwa fakta dicatat dan melakukan operasi yang tidak valid pada waktu kompilasi akan mengharuskannya tidak const fn .

Bukankah ini akan menyelesaikan masalah?

pub trait Zero {
    fn zero() -> Self;
}

pub trait ConstZero: Zero {
    const fn zero() -> Self;
}

impl<T: ConstZero> Zero for T {
    fn zero() -> Self {
        <Self as ConstZero>::zero()
    }
}

Boilerplate dapat dikurangi dengan makro.

Terlepas dari ketidaknyamanan kecil karena memiliki dua sifat terpisah ( Zero dan ConstZero ) yang melakukan hal yang hampir persis sama, saya melihat satu masalah potensial saat menggunakan implementasi selimut:

// Blanket impl
impl<T: ConstZero> Zero for T {
    fn zero () -> Self { T::const_zero() }
}

pub struct Vector2<T> {
    pub x: T,
    pub y: T,
}

impl<T: ConstZero> ConstZero for Vector2<T> {
    const fn const_zero () -> Vector2<T> {
        Vector2 { x: T::const_zero(), y: T::const_zero() }
    }
}

// Error: This now conflicts with the blanket impl above because Vector2<T> implements ConstZero and therefore Zero.
impl<T: Zero> Zero for Vector2<T> {
    fn zero () -> Vector2<T> {
        Vector2 { x: T::zero(), y: T::zero() }
    }
}

Kesalahan akan hilang jika kita menghapus blanket impl. Secara keseluruhan, ini mungkin yang paling mudah untuk diimplementasikan dalam kompiler karena menambahkan kompleksitas bahasa yang paling sedikit.

Tetapi jika kita dapat menambahkan const ke metode yang diterapkan di mana tidak diperlukan, kita dapat menghindari duplikasi ini, meskipun masih belum sempurna:

impl<T: Zero> Zero for Vector2<T> {
    const fn zero () -> Vector2<T> {
        Vector2 { x: T::zero(), y: T::zero() }
    }
}

IIRC, C++ memungkinkan sesuatu seperti ini ketika bekerja dengan constexpr . Kelemahannya di sini adalah const ini hanya akan berlaku jika <T as Zero>::zero juga const . Haruskah ini menjadi kesalahan, atau haruskah kompiler mengabaikan const ini ketika tidak berlaku (seperti C++)?

Tak satu pun dari contoh-contoh ini mengatasi masalah dengan sempurna, tetapi saya tidak dapat benar-benar memikirkan cara yang lebih baik.

Sunting: Saran @andersk akan memungkinkan contoh pertama tanpa kesalahan. Ini mungkin akan menjadi solusi terbaik/paling sederhana sejauh implementasi kompiler berjalan.

@Daggerbot Ini terdengar seperti kasus penggunaan untuk aturan "kisi" yang diusulkan di dekat akhir RFC 1210 (spesialisasi) . Jika Anda menulis

impl<T: ConstZero> Zero for T {…}  // 1
impl<T: ConstZero> ConstZero for Vector2<T> {…}  // 2
impl<T: Zero> Zero for Vector2<T> {…}  // 3
impl<T: ConstZero> Zero for Vector2<T> {…}  // 4

kemudian meskipun 1 tumpang tindih dengan 3, persimpangan mereka ditutupi dengan tepat oleh 4, sehingga akan diizinkan di bawah aturan kisi.

Lihat juga http://smallcultfollowing.com/babysteps/blog/2016/09/24/intersection-impls/.

Itu adalah sistem yang sangat kompleks, yang ingin kami hindari.

Ya, aturan kisi akan dibutuhkan.

@eddyb apa yang Anda anggap rumit?

@Kixunil Menduplikasi hampir setiap sifat di perpustakaan standar, alih-alih "hanya" menandai beberapa impl s sebagai const fn .

Kami keluar jalur di sini. Saat ini masalahnya adalah tentang menstabilkan penggunaan const fn . Mengizinkan metode sifat const fn atau const impl Trait for Foo saling ortogonal dan dengan RFC yang diterima.

@oli-obk Ini bukan RFC baru tetapi masalah pelacakan untuk const fn .

Saya baru saja memperhatikan, dan mengedit komentar saya.

@eddyb ya, tapi ini lebih sederhana untuk kompiler (minus spesialisasi, tapi kami mungkin akan tetap menginginkan spesialisasi) dan memungkinkan orang untuk terikat oleh ConstTrait juga.

Bagaimanapun, saya tidak menentang menandai impls sebagai const. Saya juga membayangkan kompiler yang menghasilkan otomatis ConstTrait: Trait .

@Kixunil Ini tidak jauh lebih sederhana, terutama jika Anda dapat melakukannya dengan spesialisasi.
Kompiler tidak perlu secara otomatis menghasilkan sesuatu seperti ConstTrait: Trait , sistem sifat juga tidak perlu tahu tentang semua ini, seseorang hanya perlu berulang melalui implementasi (baik impl konkret atau where terikat) yang disediakan oleh sistem sifat dan memeriksanya.

Saya bertanya-tanya apakah const fns harus melarang akses ke UnsafeCell . Mungkin perlu untuk mengizinkan perilaku const yang sebenarnya:

const fn dont_change_anything(&self) -> bool {
    let old = self.cell.get();
    self.cell.set(!old);
    old
}

Sejauh ini saya telah melihat bahwa set bukan const . Pertanyaannya adalah apakah ini akan bertahan selamanya. Dengan kata lain: Dapatkah kode unsafe mengandalkan bahwa ketika menjalankan fungsi const yang sama pada data yang tidak dapat diubah akan selalu mengembalikan hasil yang sama hari ini dan di setiap rilis bahasa/perpustakaan mendatang?

const fn tidak berarti tidak dapat diubah, artinya dapat dipanggil pada waktu kompilasi.

Saya melihat. Saya akan sangat menghargai jika saya entah bagaimana bisa menjamin bahwa suatu fungsi selalu mengembalikan hal yang sama ketika dipanggil beberapa kali tanpa menggunakan sifat unsafe , jika mungkin entah bagaimana.

@Kixunil yang Anda inginkan https://github.com/rust-lang/rfcs/issues/1631

@jethrogb Terima kasih atas tautannya!

Saya perhatikan bahwa mem::size_of diimplementasikan sebagai const fn pada malam hari. Apakah ini mungkin untuk mem::transmute dan lainnya? Instrinsik karat beroperasi secara internal ke kompiler, dan saya tidak cukup akrab untuk membuat perubahan yang tepat untuk memungkinkan ini. Jika tidak, saya akan dengan senang hati menerapkannya.

Sayangnya mengoperasikan nilai sedikit lebih sulit daripada hanya secara ajaib menciptakan beberapa. transmute membutuhkan miri. Langkah pertama untuk memasukkan miri ke dalam kompiler sudah berlangsung: #43628

Jadi! Ada minat untuk menstabilkan *Cell::new , mem::{size,align}_of , ptr::null{,_mut} , Atomic*::new , Once::new dan {integer}::{min,max}_value ? Haruskah kita memiliki FCP di sini atau membuat masalah pelacakan individu?

Ya.

Saya bukan bagian dari tim mana pun yang memiliki kekuatan keputusan dalam hal ini, tetapi pendapat pribadi saya adalah bahwa semua ini kecuali mem::{size,align}_of cukup sepele sehingga mereka dapat distabilkan sekarang tanpa melalui gerakan stempel karet FCP.

Sebagai pengguna saya ingin menggunakan mem::{size,align}_of dalam ekspresi const sesegera mungkin, tetapi saya telah membaca @nikomatsakis mengungkapkan kekhawatiran tentang mereka menjadi insta-const-stable ketika dibuat const fn s . Saya tidak tahu apakah ada masalah khusus atau hanya kewaspadaan umum, tetapi IIRC inilah mengapa gerbang fitur per fungsi ditambahkan. Saya membayangkan kekhawatiran untuk keduanya akan cukup mirip sehingga mereka dapat berbagi FCP. Saya tidak tahu apakah @rustbot dapat menangani FCP terpisah di utas GitHub yang sama, jadi mungkin lebih baik membuka masalah terpisah.

@durka dapatkah Anda membuka satu masalah pelacakan untuk menstabilkan keteguhan semua fungsi itu? Saya akan mengusulkan FCP setelah itu.

Untuk mengikuti petunjuk dalam diskusi tentang const fns di alloc::Layout :
Bisakah kepanikan diizinkan dalam const fn dan diperlakukan sebagai kesalahan kompilasi? Ini mirip dengan apa yang dilakukan sekarang dengan ekspresi aritmatika konstan, bukan?

Ya, itu adalah fitur yang sangat sepele setelah miri digabungkan

Apakah ini tempat yang tepat untuk meminta fungsi std tambahan menjadi const ? Jika demikian, Duration:: { new , from_secs , from_millis } seharusnya aman untuk menghasilkan const .

@remexre Cara termudah untuk mewujudkannya mungkin dengan membuat PR dan meminta tinjauan tim lib di sana.

PR sebagai https://github.com/rust-lang/rust/pull/47300. Saya juga menambahkan const ke konstruktor yang tidak stabil saat saya melakukannya.

Adakah pemikiran untuk mengizinkan fungsi std lebih lanjut dideklarasikan const ? Khususnya, mem::uninitialized dan mem::zeroed ? Saya yakin keduanya adalah kandidat yang cocok untuk fungsi const tambahan. Satu-satunya kelemahan yang dapat saya pikirkan adalah kelemahan yang sama dari mem::uninitialized , di mana pembuatan struct yang mengimplementasikan Drop dibuat dan ditulis ulang tanpa ptr::write .

Saya dapat melampirkan PR juga jika ini terdengar cocok.

Apa motivasi untuk itu? Sepertinya footgun yang tidak berguna untuk memungkinkan pembuatan pola bit yang tidak valid yang kemudian tidak dapat ditimpa (karena mereka dalam const), tapi mungkin saya mengabaikan yang sudah jelas.

mem::uninitialized benar-benar sebuah footgun, salah satu yang menembak melalui tangan Anda juga jika ditujukan dengan tidak tepat. Serius, saya tidak dapat melebih-lebihkan betapa sangat berbahayanya penggunaan fungsi ini, meskipun ditandai sebagai unsafe .

Motivasi di balik mendeklarasikan fungsi tambahan ini const berasal dari sifat fungsi ini, karena memanggil mem::uninitialized<Vec<u32>> akan mengembalikan hasil yang sama setiap saat, tanpa efek samping. Jelas jika dibiarkan tidak diinisialisasi, ini adalah hal yang mengerikan untuk dilakukan. Oleh karena itu, unsafe masih ada.

Tetapi untuk kasus penggunaan, pertimbangkan timer global, yang melacak awal dari beberapa fungsi. Status internalnya akan ditentukan di lain waktu, tetapi kita memerlukan cara untuk menampilkannya sebagai struct global statis yang dibuat saat dieksekusi.

use std::time::Instant;

pub struct GlobalTimer {
    time: UnsafeCell<Instant>
}

impl TimeManager {
    pub const fn init() -> TimeManager {
        TimeManager {
            time: UnsafeCell::new(Instant::now())
        }
    }
}

Kode ini tidak dapat dikompilasi, karena Instant::now() bukan fungsi const . Mengganti Instant::now() dengan mem::uninitialized::<Instant>()) akan memperbaiki masalah ini jika mem::uninitialized adalah const fn . Idealnya, pengembang akan menginisialisasi struktur ini setelah program mulai dieksekusi. Dan sementara kode ini dianggap sebagai karat yang tidak idiomatis (keadaan global umumnya sangat buruk), ini hanyalah salah satu dari banyak kasus di mana struktur statis global berguna.

Saya pikir posting ini memberikan dasar yang baik untuk masa depan kode Rust yang dijalankan pada waktu kompilasi. Struktur statis waktu kompilasi global adalah fitur dengan beberapa kasus penggunaan penting (OS juga muncul dalam pikiran) yang saat ini hilang. Langkah-langkah kecil dapat dilakukan menuju tujuan ini dengan berpikir perlahan menambahkan const ke fungsi perpustakaan yang dianggap cocok, seperti mem::uninitialized dan mem::zeroed , terlepas dari tanda unsafe mereka.

Sunting: Lupa const dalam tanda tangan fungsi TimeManager::init()

Hmm, kode itu dikompilasi jadi saya masih kehilangan motivasi yang tepat di sini ... jika Anda bisa menulis kode seperti

const fn foo() -> Whatever {
    unsafe { 
        let mut it = mem::uninitialized();
        init_whatever(&mut it);
        it
    }
}

Tetapi const fns saat ini sangat dibatasi sehingga Anda bahkan tidak dapat menulisnya ...

Saya menghargai pembenaran teoretis, tetapi const tidak sama dengan pure dan saya tidak berpikir kita harus melakukan apa pun untuk mendorong penggunaan fungsi-fungsi ini jika tidak diperlukan untuk beberapa kasus penggunaan yang menarik .

Saya pikir ada banyak buah gantung yang lebih rendah yang bisa distabilkan terlebih dahulu. Tanpa miri, intrinsik yang tidak diinisialisasi dan dipusatkan tidak masuk akal. Saya ingin melihat mereka suatu hari nanti. Kami bahkan awalnya dapat menstabilkan mereka dan mengharuskan semua konstanta harus menghasilkan hasil yang diinisialisasi, bahkan jika perhitungan menengah dapat tidak diinisialisasi.

Yang mengatakan, dengan serikat pekerja dan kode tidak aman Anda dapat meniru yang tidak diinisialisasi atau dinolkan, jadi tidak ada gunanya menjaga mereka tetap non-const

Dengan bantuan union s, kode sebelumnya sekarang dikompilasi . Ini benar-benar menakutkan .

Semua poin bagus juga. Fungsi intrinsik ini cukup rendah dalam daftar kasus penggunaan, tetapi mereka masih merupakan kandidat yang cocok untuk const -ness.

Itu sangat menakjubkan.

Jadi... mengapa sebenarnya Anda menganjurkan untuk membatasi mem::uninitialized, as
menentang, katakanlah, Instan::sekarang? :)

Kebutuhan untuk memiliki inisialisasi konstan untuk struct dengan non-konstan
interiornya nyata (lihat: Mutex). Tapi saya tidak berpikir membuat malarkey ini
lebih mudah adalah cara yang tepat untuk mendapatkannya!

Pada Kam, 25 Jan 2018 pukul 02.21, Stephen Fleischman <
[email protected]> menulis:

Dengan bantuan serikat, kode sebelumnya sekarang dikompilasi
https://play.rust-lang.org/?gist=be075cf12f63dee3b2e2b65a12a3c854&version=nightly .
Ini benar-benar menakutkan .


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/rust-lang/rust/issues/24111#issuecomment-360382201 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAC3n-HyWD6MUEbfHkUUXonh9ORGPSRoks5tOCtegaJpZM4D66IA
.

Instant::now tidak dapat menjadi konstanta. Apa yang akan dikembalikan oleh fungsi itu? Waktu kompilasi?

Dapatkah seseorang meringkas apa yang perlu dilakukan untuk menstabilkan ini? Keputusan apa yang perlu dicapai? Apakah akan menstabilkan ini sama sekali?

Integrasi dengan pola (mis. https://Gist.github.com/d0ff1de8b6fc15ef1bb6)

Saya sudah mengomentari intinya, tetapi mengingat const fn saat ini tidak dapat dicocokkan dengan suatu pola, ini seharusnya tidak memblokir stabilisasi, bukan? Kami selalu bisa mengizinkannya setelah itu jika masuk akal.

Instan::sekarang tidak dapat menjadi const. Apa yang akan dikembalikan oleh fungsi itu? Waktu kompilasi?

Tapi mungkin ada Instant::zero() atau Instant::min_value() yang merupakan const.

Dapatkah seseorang meringkas apa yang perlu dilakukan untuk menstabilkan ini? Keputusan apa yang perlu dicapai? Apakah akan menstabilkan ini sama sekali?

Saya pikir satu-satunya pertanyaan terbuka adalah apakah pemeriksaan const fn kami cukup ketat untuk tidak secara tidak sengaja mengizinkan/menstabilkan sesuatu yang tidak kami inginkan di dalam const fn.

Bisakah kita melakukan integrasi dengan pola melalui rust-lang/rfcs#2272 ? Pola sudah menyakitkan seperti saat ini, jangan membuatnya lebih menyakitkan.

Saya pikir satu-satunya pertanyaan terbuka adalah apakah pemeriksaan const fn kami cukup ketat untuk tidak secara tidak sengaja mengizinkan/menstabilkan sesuatu yang tidak kami inginkan di dalam const fn.

Perbaiki saya jika saya salah, tetapi bukankah pemeriksaan itu identik dengan pemeriksaan yang saat ini diizinkan di badan nilai const ? Saya mendapat kesan bahwa const FOO: Type = { body }; dan const FOO: Type = foo(); const fn foo() -> Type { body } identik dalam apa yang mereka izinkan untuk badan sewenang-wenang

@sgrif Saya pikir kekhawatirannya adalah seputar argumen, yang dimiliki const fn tetapi const tidak.
Juga, tidak jelas apakah dalam jangka panjang kami ingin mempertahankan sistem const fn hari ini.

Apakah Anda menyarankan obat generik const (dua arah) sebagai gantinya? (misalnya <const T> + const C<T> alias const C<const T> ?)

Saya benar-benar ingin memiliki makro try_const! yang akan mencoba mengevaluasi ekspresi apa pun pada waktu kompilasi, dan panik jika tidak memungkinkan. Makro ini akan dapat memanggil non-const fns (menggunakan miri?), jadi kita tidak perlu menunggu sampai setiap fungsi di std telah ditandai const fn. Namun seperti namanya, itu bisa gagal kapan saja, jadi jika suatu fungsi diperbarui dan sekarang tidak bisa menjadi const, itu akan berhenti dikompilasi.

@ Badel2 Saya mengerti mengapa Anda menginginkan fitur seperti itu, tetapi saya menduga penggunaannya secara luas akan sangat buruk bagi ekosistem peti. Karena dengan cara ini peti Anda mungkin berakhir tergantung pada fungsi di peti lain yang waktu kompilasinya dapat dievaluasi, dan kemudian pembuat peti mengubah sesuatu yang tidak memengaruhi tanda tangan fungsi tetapi mencegah fungsi tersebut dari waktu kompilasi yang dapat dievaluasi.

Jika fungsi ditandai const fn di tempat pertama, maka pembuat peti akan melihat masalah secara langsung ketika mencoba untuk mengkompilasi peti dan Anda dapat mengandalkan anotasi.

Kalau saja ini berhasil di taman bermain... https://play.rust-lang.org/?gist=6c0a46ee8299e36202f959908e8189e6&version=stable

Ini adalah cara non-portabel (memang, sangat non-portabel sehingga berfungsi di sistem saya tetapi tidak di taman bermain - namun keduanya linux) cara memasukkan waktu pembuatan dalam program yang dibangun.

Cara portabel adalah dengan mengizinkan SystemTime::now() dalam evaluasi const.

(Ini adalah argumen untuk const/compile-time-eval dari fungsi/ekspresi APAPUN, terlepas dari apakah itu const fn atau tidak.)

Bagi saya itu terdengar seperti argumen untuk melarang jalur absolut di include_bytes

Jika Anda mengizinkan SystemTime::now di const fn, const FOO: [u8; SystemTime::now()] = [42; SystemTime::now()]; akan secara acak kesalahan tergantung pada kinerja sistem Anda, penjadwal dan posisi Jupiter.

Lebih buruk lagi:

const TIME: SystemTime = SystemTime::now();

Tidak berarti nilai TIME sama di semua situs penggunaan, terutama di seluruh kompilasi dengan inkremental dan lintas peti.

Dan yang lebih gila lagi adalah Anda dapat mengacaukan foo.clone() dengan cara yang sangat tidak sehat, karena Anda mungkin akan memilih klon impl dari larik dengan panjang 3 tetapi tipe yang dikembalikan mungkin berupa larik dengan panjang 4.

Jadi, bahkan jika kami mengizinkan fungsi arbitrer dipanggil, kami tidak akan pernah mengizinkan SystemTime::new() untuk berhasil kembali, sama seperti kami tidak akan pernah mengizinkan generator angka acak yang sebenarnya

@SoniEx2 Saya kira ini agak di luar topik di sini, tetapi Anda dapat mengimplementasikan sesuatu seperti itu hari ini menggunakan file kargo build.rs . Lihat Build Scripts di Cargo Book, khususnya bagian studi kasus pembuatan kode.

@oli-obk Saya pikir itu bukan masalah yang sepenuhnya sama karena yang satu adalah tentang keamanan API versi sementara yang lain tentang lingkungan build, namun saya setuju bahwa keduanya dapat menyebabkan kerusakan ekosistem jika tidak diterapkan dengan hati-hati.

Tolong jangan izinkan mendapatkan waktu saat ini dalam const fn ; kita tidak perlu menambahkan lebih banyak/cara yang lebih mudah untuk membuat build tidak dapat direproduksi.

Kami tidak dapat mengizinkan segala jenis non-determinisme (seperti angka acak, waktu saat ini, dll) menjadi const fn - memungkinkan yang mengarah ke jenis sistem yang tidak sehat karena rustc mengasumsikan bahwa ekspresi konstan selalu mengevaluasi ke hasil yang sama yang diberikan masukan yang sama. Lihat di sini untuk sedikit penjelasan lebih lanjut.

Metode masa depan untuk menangani kasus seperti di https://github.com/rust-lang/rust/issues/24111#issuecomment -376352844 adalah dengan menggunakan makro prosedural sederhana yang mendapatkan waktu saat ini dan memancarkannya sebagai angka biasa atau tanda tali. Makro prosedural adalah kode yang kurang lebih sepenuhnya tidak dibatasi yang bisa mendapatkan waktu dengan salah satu cara portabel yang biasa digunakan kode Rust non-const.

@rfcbot menggabungkan fcp

Saya mengusulkan kita menggabungkan ini, karena ini adalah opsi yang agak waras, bukan perubahan yang melanggar, mencegah perubahan yang tidak disengaja (mengubah fungsi dengan cara yang membuatnya tidak dapat dievaluasi sementara peti lain menggunakan fungsi dalam konteks const) dan satu-satunya hal yang sangat buruk tentang itu adalah kita harus membuang const sebelum sekelompok deklarasi fungsi.

@rfcbot fcp bergabung atas nama @oli-obk - tampaknya layak untuk dipikirkan tentang stabilisasi dan mendiskusikan masalah

Anggota tim @nrc telah mengusulkan untuk menggabungkan ini. Langkah selanjutnya adalah peninjauan oleh sisa tim yang ditandai:

  • [x] @aturon
  • [x] @cramertj
  • [ ] @eddyb
  • [x] @joshtriplett
  • [ ] @nikomatsakis
  • [x] @nrc
  • [ ] @pnkfelix
  • [x] @scottmcm
  • [x] @withoutboats

Kekhawatiran:

  • desain (https://github.com/rust-lang/rust/issues/24111#issuecomment-376829588)
  • parallel-const-traits diselesaikan dengan https://github.com/rust-lang/rust/issues/24111#issuecomment -377133537
  • prioritas (https://github.com/rust-lang/rust/issues/24111#issuecomment-376652507)
  • runtime-pointer-addresses (https://github.com/rust-lang/rust/issues/24111#issuecomment-386745312)

Setelah mayoritas pengulas menyetujui (dan tidak ada yang keberatan), ini akan memasuki periode komentar terakhirnya. Jika Anda menemukan masalah besar yang belum diangkat pada titik mana pun dalam proses ini, silakan angkat bicara!

Lihat dokumen ini untuk info tentang perintah apa yang dapat diberikan oleh anggota tim yang ditandai kepada saya.

@rfcbot prioritas perhatian

Kami mungkin ingin menyepakati ini sampai setelah edisi karena saya tidak berpikir kami memiliki bandwidth untuk menangani dampak apapun.

@rfcbot memperhatikan segalanya-const

Kita berakhir di sedikit dunia C++ di mana ada insentif untuk membuat setiap fungsi yang Anda bisa const .

Ringkasan diskusi singkat dengan @oli-obk:

  1. Di masa depan, hampir setiap fungsi dapat ditandai const . Misalnya, semua yang ada di Vec bisa menjadi const . Di dunia itu, mungkin masuk akal untuk menyingkirkan kata kunci const sama sekali: hampir semuanya bisa const , dan seseorang harus berusaha keras untuk mengubah fungsi dari const ke non -const, jadi bahaya kompatibilitas mundur tentang constness yang disimpulkan mungkin tidak akan terlalu tinggi.

  2. Namun, menyingkirkan const hari ini tidak mungkin dilakukan. Miri hari ini tidak dapat menafsirkan semuanya , dan tidak benar-benar diuji secara menyeluruh dalam produksi.

  3. Ini sebenarnya kompatibel ke belakang untuk meminta const hari ini, dan kemudian menghentikan kata kunci ini dan beralih ke constness yang disimpulkan di masa depan.

Menempatkan 1, 2 dan 3 bersama-sama, tampaknya opsi yang bagus untuk menstabilkan kata kunci const hari ini, daripada memperluas set fungsi yang dapat dievaluasi secara konstan di rilis mendatang. Setelah beberapa waktu, kami akan memiliki evaluator konstanta hony badger yang teruji pertempuran, yang dapat mengevaluasi semuanya. Pada titik itu, kita dapat beralih ke const yang disimpulkan.

Wrt bahaya kejatuhan: const fn telah digunakan secara liar pada malam hari, terutama pada tertanam. Juga, pemeriksa const fn adalah pemeriksa yang sama dengan yang digunakan untuk inisialisasi dan konstanta statis (kecuali untuk beberapa pemeriksaan khusus statis dan argumen fungsi).

Kerugian utama yang saya lihat adalah bahwa kami pada dasarnya menganjurkan untuk menyemprotkan const secara bebas ke seluruh peti (untuk saat ini, lihat posting @matklad untuk ide-ide masa depan

@rfcbot memperhatikan sifat-sifat paralel

Rasanya seperti menstabilkan ini akan segera menghasilkan sekelompok peti yang membuat hierarki sifat paralel dengan Const di depan: ConstDefault , ConstFrom , ConstInto , ConstClone , ConstTryFrom , ConstTryInto , dll dan meminta ConstIndex dan semacamnya. Itu tidak buruk -- kita tentu memiliki sedikit dengan Try hari ini, meskipun menstabilkan TryFrom akan membantu -- tetapi saya merasa akan lebih baik untuk setidaknya memiliki sketsa rencana untuk menyelesaikannya dengan lebih baik. (Apakah itu https://github.com/rust-lang/rfcs/pull/2237? Saya tidak tahu).

( @nrc : Sepertinya bot hanya mendaftarkan salah satu masalah Anda)

Paralel-const-traits memiliki solusi trivial dalam versi hipotetis masa depan const-all-the-things. Mereka hanya akan bekerja.

Di dunia const fn Anda tidak akan berakhir dengan duplikasi sifat, selama kami tidak mengizinkan metode sifat const fn (yang tidak kami lakukan), hanya karena Anda tidak bisa. Anda tentu saja dapat membuat konstanta terkait (pada malam hari) yang merupakan jenis situasi libstd setahun yang lalu, di mana kami memiliki banyak konstanta untuk menginisialisasi berbagai jenis di dalam statika/konstanta, tanpa mengekspos bidang pribadinya. Tapi itu adalah sesuatu yang mungkin sudah terjadi untuk sementara waktu dan tidak terjadi.

Untuk lebih jelasnya, ConstDefault sudah dimungkinkan hari ini tanpa const fn , dan contoh lainnya ( ConstFrom , ConstInto , ConstClone , ConstTryFrom , ConstTryInto ) tidak akan mungkin bahkan dengan fitur ini distabilkan, karena tidak menambahkan metode sifat const seperti yang disebutkan @oli-obk.

( ConstDefault dimungkinkan dengan menggunakan const terkait daripada const fn terkait, tetapi sejauh yang saya tahu setara dengan daya.)

@scottmcm const fn dalam definisi sifat tidak mungkin hari ini (oh @solson sudah menyebutkannya).

@eddyb ide acak: bagaimana jika kita memungkinkan const impl suatu sifat alih-alih menambahkan const fn dalam definisi sifat? (Keduanya juga tidak saling eksklusif.)

@whitequark https://github.com/rust-lang/rfcs/pull/2237 mencakup gagasan itu, melalui kombinasi const impl berkembang menjadi const fn pada setiap fn di impl , dan mengizinkan impl dengan semua const metode untuk memenuhi T: const Trait terikat, tanpa menandai salah satu metode const di definisi sifat itu sendiri.

@rfcbot perhatian desain

Kami secara historis pernah mencoba menstabilkan sistem const fn tertentu karena beberapa alasan:

  • yang saat ini tidak mendukung trait s yang memerlukan metode const fn , atau sifat impl s yang menyediakan metode const fn (lihat https://github. com/rust-lang/rfcs/pull/2237 untuk beberapa cara melakukannya)
  • terkait, ada masalah meminta metode yang digunakan melalui T: Trait terikat menjadi const fn tanpa memiliki sifat yang terpisah, dan sebaiknya hanya digunakan pada waktu kompilasi (misalnya Option::map akan bekerja sama saat runtime tetapi membutuhkan penutupan const-callable di CTFE)
  • dengan banyak algoritme, koleksi, dan abstraksi yang berpotensi dapat dievaluasi secara const, mungkin ada seluruh peti yang akan menggunakan const fn di mana-mana ( libcore terlintas dalam pikiran)

Ada pilihan desain yang berbeda yang akan meringankan sebagian besar atau semua masalah ini (dengan biaya memperkenalkan yang lain), misalnya ini adalah beberapa yang muncul:

  • tidak memerlukan anotasi apa pun dan hanya memancarkan kesalahan kompiler dan miri gagal untuk mengevaluasi

    • pro: basis kode yang lebih bersih, evaluasi konstan sudah bisa gagal tergantung pada nilai yang terlibat

    • kontra: tidak ada dokumentasi perilaku tingkat bahasa dan batas semver, Anda dapat melempar kode orang lain ke miri dan mengamati perubahan kecil yang mereka buat, misalnya runtime debug logging ditambahkan dalam versi patch dari salah satu dependensi Anda; juga, promosi nilai lebih sulit dilakukan

  • cara untuk memilih perilaku di atas per fungsi/impl/modul/dll.

    • badan fungsi ini akan (setidaknya dalam hal const fn ) berperilaku seperti makro

    • pro: tidak memiliki implikasi yang parah, membatasi ruang lingkup beberapa analisis

    • kontra: masih bukan dokumentasi yang bagus, tidak jelas perilaku apa yang harus terpengaruh ( hanya const-evaluatability?), setiap perubahan dalam tubuh dapat dianggap sebagai pemecah semver

Karena dengan cara ini peti Anda mungkin berakhir tergantung pada fungsi di peti lain yang waktu kompilasinya dapat dievaluasi, dan kemudian pembuat peti mengubah sesuatu yang tidak memengaruhi tanda tangan fungsi tetapi mencegah fungsi tersebut dari waktu kompilasi yang dapat dievaluasi.

@leoschwarz bukankah ini sudah menjadi masalah dengan ciri-ciri otomatis? Mungkin solusinya adalah dengan mengintegrasikan rust-semverver dengan kargo untuk mendeteksi kerusakan yang tidak diinginkan semacam ini.

Yang mengatakan, tidak jelas bagi saya apa yang terjadi jika miri memiliki batas waktu evaluasi yang Anda (sebagai penulis perpustakaan) secara tidak sengaja melebihi, menyebabkan kegagalan kompilasi di hilir.

@nrc Saya pikir "semuanya-const" benar, tetapi bukan masalah. Ya, pada akhirnya kita akan menandai banyak hal const .

Hanya ingin menunjukkan bahwa saya tidak yakin saya ingin semuanya disimpulkan menjadi
konst. Ini adalah keputusan tentang apakah runtime atau waktu kompilasi lebih
penting. Terkadang saya pikir kompiler melakukan perhitungan yang cukup di
waktu kompilasi!

Pada Rabu, 28 Maret 2018 pukul 14:49, Josh Triplett [email protected]
menulis:

@nrc https://github.com/nrc Saya pikir "semuanya-const" benar, tapi tidak
sebuah isu. Ya, kita akan berakhir menandai petak besar hal const.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/rust-lang/rust/issues/24111#issuecomment-376914220 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAC3ny9Wm9JK6p-fXf6gbaEgjFtBpMctks5ti6LigaJpZM4D66IA
.

batas waktu evaluasi

batas itu segera hilang.

Hanya ingin menunjukkan bahwa saya tidak yakin saya ingin semuanya disimpulkan menjadi const.

Oh tidak, kami tidak akan menghitung secara acak pada waktu kompilasi. Biarkan saja hal-hal acak dihitung dalam tubuh statika, konstanta, diskriminan varian enum, dan panjang array

@rfcbot menyelesaikan sifat-sifat paralel

Terima kasih atas koreksinya, teman-teman!

batas itu segera hilang.

Luar biasa. Dalam hal ini, auto-const-fn (dalam kombinasi dengan beberapa integrasi rust-semverver atau serupa untuk memberikan informasi tentang kerusakan) terdengar luar biasa, meskipun "tambahkan logging dan sebabkan kerusakan" bisa menjadi masalah. Meskipun Anda dapat menabrak nomor versi, saya kira, itu tidak seperti mereka terbatas.

Pencatatan dan pencetakan adalah efek samping "baik" dalam model konstanta saya. Kami akan dapat menemukan solusi untuk itu jika semua orang setuju. Kami bahkan dapat menulis ke file (tidak juga, tetapi bertindak seolah-olah kami melakukannya dan membuang semuanya).

Saya benar-benar khawatir tentang membuang efek samping secara diam-diam.

Kita bisa mendiskusikannya begitu kita membuat RFC di sekitar mereka. Untuk saat ini Anda tidak dapat memiliki "efek samping" dalam konstanta. Topiknya ortogonal untuk menstabilkan konstanta fn

Saya agak khawatir tentang pendekatan "lakukan saja peringatan semver" untuk disimpulkan
keteguhan. Jika seorang penulis peti yang tidak pernah memikirkan keteguhan melihat
"peringatan: perubahan yang baru saja Anda buat tidak memungkinkan untuk memanggil foo() di
const konteks, yang sebelumnya mungkin", apakah mereka akan melihatnya sebagai
non-sequitur dan membungkamnya? Jelas, orang-orang dalam masalah ini sering berpikir
tentang fungsi mana yang bisa menjadi const. Dan alangkah baiknya jika lebih banyak orang melakukannya
itu (setelah const_fn stabil). Tapi apakah peringatan tiba-tiba itu benar?
cara untuk mendorong itu?

Pada Kam, 29 Mar 2018 pukul 04.36 , Oliver [email protected]
menulis:

Kita bisa mendiskusikannya begitu kita membuat RFC di sekitar mereka. Untuk saat ini kamu hanya
tidak dapat memiliki "efek samping" dalam konstanta. Topiknya ortogonal dengan
menstabilkan konstanta fn


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/rust-lang/rust/issues/24111#issuecomment-377164275 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAC3n3MtmvrDF42Iy0nhZ2q8xC-QGcvXks5tjJ0ggaJpZM4D66IA
.

Saya pikir const fn eksplisit dapat mengganggu dan mengacaukan banyak API, namun saya pikir alternatif dari asumsi implisit memiliki terlalu banyak masalah untuk dapat dipraktikkan:

  • Membuang efek samping dapat menyebabkan kode berperilaku berbeda (misalnya menulis dan kemudian membaca file) jika disebut const atau non const.
  • Memanggil fungsi eksternal berarti selalu ada efek samping, jadi kode yang tidak aman mungkin tidak akan pernah dapat disimpulkan const fn.
  • Kapan const fn dapat disimpulkan untuk kode generik? Apakah ini akan dilakukan pada waktu monomorfisasi?

Namun saya benar-benar melihat masalah terbesar dalam tidak membuatnya secara eksplisit adalah bahwa seseorang dapat secara tidak sengaja memecahkan banyak kode dengan satu perubahan tanpa menyadarinya. Ini terutama menjadi perhatian dengan grafik ketergantungan panjang yang umum di ekosistem Rust. Jika memerlukan perubahan eksplisit pada tanda tangan fungsi, seseorang akan menyadari bahwa ini adalah perubahan yang melanggar dengan lebih mudah.

Mungkin fitur seperti itu dapat diimplementasikan sebagai flag konfigurasi tingkat peti yang dapat ditambahkan di akar peti, #![infer_const_fn] atau sesuatu seperti itu, dan tetap ikut serta selamanya. Jika bendera ditambahkan const fn akan disimpulkan jika memungkinkan di peti dan juga tercermin dalam dokumen (dan itu akan mengharuskan fungsi yang dipanggil adalah const fn juga), jika pembuat peti menambahkan bendera ini, mereka berjanji untuk berhati-hati tentang versi dan mungkin rust-semverver bahkan bisa dipaksakan.

Bagaimana dengan melakukannya secara terbalik?

Daripada memiliki const fn, memiliki sisi fn.

Itu masih eksplisit (Anda perlu meletakkan sisi fn untuk memanggil sisi fn, secara eksplisit melanggar kompatibilitas), dan menghilangkan kekacauan. (Beberapa) intrinsik dan apa pun dengan asm akan menjadi fn samping.

Itu tidak kompatibel, meskipun saya kira itu dapat ditambahkan dalam edisi?

Pada 30 Maret 2018 2:43:06 GMT+08:00, "Soni L." [email protected] menulis:

Bagaimana dengan melakukannya secara terbalik?

Daripada memiliki const fn, memiliki sisi fn.

Itu masih eksplisit (Anda harus meletakkan sisi fn untuk memanggil sisi fn,
secara eksplisit melanggar kompatibilitas), dan menghilangkan kekacauan. (Beberapa)
intrinsik dan apa pun dengan asm akan menjadi fn samping.

--
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung atau lihat di GitHub:
https://github.com/rust-lang/rust/issues/24111#issuecomment -377333542

--
Dikirim dari perangkat Android saya dengan K-9 Mail. Mohon maafkan singkatnya saya.

Saya pikir masalah yang lebih besar adalah bahwa itu akan menjadi kejutan nyata bagi pemula, karena bukan itu yang dilakukan sebagian besar bahasa pemrograman.

@whitequark Saya tidak setuju dengan apa pun yang hanya melakukan itu ("membuang efek samping"), saya pikir @oli-obk berbicara tentang perpanjangan masa depan tetapi dari diskusi yang saya ikuti, saya tahu yang berikut

  • kami dapat membedakan "efek samping deterministik" (yang tidak mengembalikan data yang tidak murni)
  • kita dapat memiliki API khusus jika kita ingin misalnya "mencatat data pada waktu kompilasi"

    • ini cukup sulit tergantung pada tingkat determinisme luar yang Anda inginkan, karena kompilasi sesuai permintaan tambahan tidak selalu menghasilkan output yang konsisten

  • secara khusus, kami tidak akan menyentuh apa pun yang saat ini ada dan menggunakan global / C FFI

EDIT : supaya diskusi tidak menggagalkan, mis:

Membuang efek samping dapat menyebabkan kode berperilaku berbeda (misalnya menulis dan kemudian membaca file) jika disebut const atau non const.

Kita bisa (mungkin?) semua menganggap @oli-obk salah bicara tentang membuang efek samping seperti itu.

Mungkin fitur seperti itu dapat diimplementasikan sebagai flag konfigurasi tingkat peti yang dapat ditambahkan di akar peti

Itu adalah bagian dari contoh kedua dari saran sebelumnya, dari https://github.com/rust-lang/rust/issues/24111#issuecomment -376829588.
Jika kami memiliki "flag konfigurasi" yang dicakup, pengguna harus dapat memilih IMO cakupan yang lebih halus.

Bagaimana dengan melakukannya secara terbalik?
Daripada memiliki const fn, memiliki sisi fn.
Itu masih eksplisit (Anda perlu meletakkan sisi fn untuk memanggil sisi fn, secara eksplisit melanggar kompatibilitas), dan menghilangkan kekacauan. (Beberapa) intrinsik dan apa pun dengan asm akan menjadi fn samping.

Di https://github.com/rust-lang/rust/issues/24111#issuecomment -376829588 Saya mencoba menunjukkan bahwa seluruh perpustakaan dapat berupa "semua const fn " atau "semua side fn ".
Jika tidak pada deklarasi fungsi, melainkan cakupan, mungkin bisa bekerja di edisi mendatang.
Namun, tanpa semantik "simpulkan dari tubuh" Anda harus merancang interaksi sifat bahkan untuk keikutsertaan side fn , jadi Anda tidak mendapatkan apa pun dan Anda berpotensi menimbulkan gesekan besar.

Bagian 3.3 dari tulisan Kenton Varda "Lajang dianggap berbahaya" tampaknya relevan di sini (jujur, semuanya layak dibaca).

Bagaimana dengan pencatatan debug?

Dalam praktiknya, semua orang mengakui bahwa debug logging harus tersedia untuk setiap bagian kode. Kami membuat pengecualian untuk itu. Dasar teoretis yang tepat untuk pengecualian ini, bagi mereka yang peduli, dapat diberikan dalam beberapa cara.

Dari sudut pandang keamanan, debug logging adalah singleton jinak. Itu tidak dapat digunakan sebagai saluran komunikasi karena hanya menulis. Dan jelas tidak mungkin menyebabkan kerusakan apa pun dengan menulis ke log debug, karena log debug bukan merupakan faktor dalam kebenaran program. Bahkan jika modul jahat "meng-spam" log, pesan dari modul itu dapat dengan mudah disaring, karena log debug biasanya mengidentifikasi dengan tepat modul apa yang menghasilkan setiap pesan (kadang-kadang mereka bahkan menyediakan jejak tumpukan). Oleh karena itu, tidak ada masalah dalam menyediakannya.

Argumen analog dapat dibuat untuk menunjukkan bahwa debug logging tidak membahayakan keterbacaan, pengujian, atau pemeliharaan.

Pembenaran teoretis lain untuk logging debug mengatakan bahwa fungsi log debug benar-benar hanya no-op yang kebetulan diamati oleh debugger. Ketika tidak ada debugger yang berjalan, fungsi tidak melakukan apa-apa. Debugging secara umum jelas merusak seluruh model kemampuan objek, tetapi juga jelas merupakan operasi yang diistimewakan.

Pernyataan saya tentang "kita dapat menemukan solusi [untuk debugging]" memang mengacu pada API potensial di masa depan, yang dapat dipanggil dari consts, tetapi memiliki beberapa bentuk pencetakan. Mengimplementasikan operasi cetak spesifik platform secara acak (hanya agar kita dapat membuat kode yang ada dengan pernyataan print/debug menjadi const) bukanlah sesuatu yang harus dilakukan oleh const evaluator. Ini akan murni ikut serta, secara eksplisit tidak memiliki perilaku berbeda yang dapat diamati (misalnya peringatan di const eval dan baris perintah/output file saat runtime). Semantik yang tepat diserahkan kepada RFC masa depan dan harus dianggap sepenuhnya ortogonal untuk konstanta fn secara umum

Apakah ada kerugian signifikan pada const impl Trait dan T: const Trait ?

Selain lebih banyak penyemprotan sekitar const , hanya beberapa metode sifat yang mungkin const, yang akan membutuhkan kontrol berbutir lebih halus. Saya tidak tahu sintaks yang rapi untuk menentukannya. Mungkin where <T as Trait>::some_method is const ( is bisa menjadi kata kunci kontekstual).

Sekali lagi, itu ortogonal terhadap const fn.

[u8; SizeOf<T>::Output]

Jika const dan side fns terpisah, kita harus mempertimbangkan desain yang sebenarnya. Cara termudah untuk memisahkannya adalah dengan membuat const fns sebagai ekstensi dari sesuatu yang kita miliki saat ini - sistem tipe turing-complete.

Atau, buat const fns benar-benar const: apa pun const fn harus dievaluasi seolah-olah setiap parameter adalah const generik.

Ini membuat mereka jauh lebih mudah untuk dipikirkan, karena saya pribadi tidak dapat bernalar tentang const fns seperti saat ini. Saya dapat bernalar tentang tipe lengkap turing, makro, fns normal, dll, tetapi saya merasa tidak mungkin untuk bernalar tentang const fn, karena bahkan detail kecil pun mengubah artinya sepenuhnya.

karena bahkan detail kecil mengubah maknanya sepenuhnya.

Bisakah Anda menguraikannya? Maksud Anda ekstensi seperti const fn pointer, const sifat batas, ...? Karena saya tidak melihat detail kecil dalam proposal const fn .

Atau, buat const fns benar-benar const: apa pun const fn harus dievaluasi seolah-olah setiap parameter adalah const generik.

Itulah yang kami lakukan pada waktu kompilasi. Hanya saja pada saat runtime fungsi tersebut digunakan seperti fungsi lainnya.

Masalahnya adalah bahwa detail kecil apa pun dapat mengubah const eval menjadi runtime eval. Ini mungkin tidak tampak seperti masalah besar, pada awalnya, tetapi bisa jadi .

Katakanlah panggilan fungsi sangat panjang karena semuanya const fns? Dan Anda ingin membaginya menjadi beberapa baris.

Jadi Anda menambahkan beberapa let s.

Sekarang program Anda membutuhkan waktu 20x lebih lama untuk dijalankan.

@SoniEx2 Ukuran array ( $N dalam [u8; $N] ) selalu dievaluasi pada waktu kompilasi. Jika ekspresi itu bukan- const , kompilasi akan gagal. Sebaliknya, let x = foo() akan memanggil foo pada saat runtime apakah itu const fn (modulo pengoptimalan inlining dan propagasi konstan, tapi itu sepenuhnya terpisah dari const fn ). Jika Anda ingin memberi nama hasil evaluasi beberapa ekspresi pada waktu kompilasi, Anda memerlukan item const .

Sekarang program Anda membutuhkan waktu 20x lebih lama untuk dijalankan.

Itu sama sekali bukan cara kerja const fn!

Jika Anda mendeklarasikan suatu fungsi const fn dan Anda menambahkan pengikatan let di dalamnya, kode Anda akan berhenti dikompilasi.

Jika Anda menghapus const dari const fn , itu adalah perubahan yang melanggar dan akan merusak semua penggunaan fungsi itu di dalam misalnya const , static atau panjang array. Kode Anda yang merupakan kode runtime dan menjalankan const fn , tidak akan pernah berjalan pada waktu kompilasi. Ini hanya panggilan fungsi runtime normal, jadi tidak menjadi lebih lambat.

Sunting: @SimonSapin mengalahkan saya untuk itu: D

Const fn dievaluasi pada waktu kompilasi jika memungkinkan.

Itu adalah,

const fn random() -> i32 {
    4
}

fn thing() -> i32 {
    let i = random(); // the RHS of this binding is evaluated at compile-time, there is no call to random at runtime.
}

Sekarang katakanlah Anda memiliki const fn yang membutuhkan argumen. Ini akan dievaluasi pada waktu kompilasi:

fn thing() {
    let x = const_fn_with_1_arg(const_fn_returns_value());
}

Ini akan menyebabkan const_fn_with_1_arg dievaluasi saat runtime:

fn thing() {
    let x = const_fn_returns_value();
    let y = const_fn_with_1_arg(x); // suddenly your program takes 20x longer to run, and compiles 20x faster.
}

@eddyb Saya ingin tahu apakah masalah desain dapat diselesaikan dengan pengamatan bahwa "minimal const fn" kompatibel dengan semua potensi ekstensi di masa mendatang? Artinya, pemahaman saya adalah bahwa kami ingin menstabilkan

menandai fungsi bebas dan metode inheren sebagai const, memungkinkannya dipanggil dalam konteks konstanta, dengan argumen konstan.

Ini tampaknya sepenuhnya kompatibel dengan desain "efek const untuk sifat". Ini juga kompatibel dengan desain "inferred const", karena kita dapat membuat const opsional nanti.

Apakah ada desain alternatif masa depan yang tidak sesuai dengan proposal "minimal const fn" saat ini?

@nrc

Perhatikan bahwa rfcbot tidak mendaftarkan masalah everything-const Anda (satu masalah per komentar!) Namun, tampaknya itu adalah bagian dari masalah desain, yang ditangani oleh komentar saya sebelumnya (TL; DR: proposal minimal saat ini sepenuhnya kompatibel dengan semuanya, kami dapat membuat kata kunci const opsional di masa mendatang).

Untuk masalah prioritas/kejatuhan, saya ingin mendokumentasikan apa yang telah kita diskusikan, dan apa yang belum kita dokumentasikan:

  • const fn foo(x: i32) -> i32 { body } adalah tambahan yang relatif kecil dari const FOO: i32 = body; , jadi risiko kejatuhannya kecil. Artinya, sebagian besar kode yang benar-benar mengimplementasikan const fn sudah bekerja keras di kompiler stabil (penafian: ini adalah sesuatu yang saya dengar dari @oli-obk, saya mungkin salah dengar).

  • kelompok kerja tertanam sangat menginginkan const fn :)

Selain itu, perhatikan bahwa tidak menstabilkan const fn menyebabkan proliferasi API suboptimal di perpustakaan, karena mereka harus menggunakan trik seperti ATOMIC_USIZE_INIT untuk mengatasi kekurangan const fns.

misalkan saya = acak(); // RHS pengikatan ini dievaluasi pada waktu kompilasi, tidak ada panggilan ke acak saat runtime.

Tidak, itu tidak terjadi sama sekali. Itu bisa terjadi (dan llvm mungkin melakukan ini), tetapi Anda tidak dapat mengharapkan pengoptimalan kompiler apa pun yang bergantung pada heuristik benar-benar terjadi. Jika Anda ingin sesuatu dihitung pada waktu kompilasi, tempelkan di const , dan Anda mendapatkan jaminan itu.

jadi const fn hanya dievaluasi dalam const, dan ini pada dasarnya tidak berguna jika tidak?

mengapa const dan non-const fn tidak dipisahkan secara ketat?

lihat semantik berantakan karena mereka sengaja mencampur waktu kompilasi dan runtime.

jadi const fn hanya dievaluasi dalam const, dan ini pada dasarnya tidak berguna jika tidak?

Mereka bukannya tidak berguna, mereka dieksekusi saat runtime seperti fungsi lainnya. Ini berarti Anda tidak perlu menggunakan "subbahasa" Rust yang berbeda tergantung apakah Anda sedang dalam evaluasi const atau tidak.

mengapa const dan non-const fn tidak dipisahkan secara ketat?

Seluruh motivasi untuk const fn adalah untuk tidak memiliki pemisahan ini. Jika tidak, kita perlu menduplikasi semua jenis fungsi: AtomicUsize::new() + AtomicUsize::const_new() , meskipun kedua badan identik.

Apakah Anda benar-benar ingin menulis 90% dari libcore dua kali, sekali untuk evaluasi konstan dan sekali untuk runtime? Hal yang sama mungkin berlaku untuk banyak peti lainnya.

Saya berpikir AtomicUsize::Of<value> . Dan ya, saya lebih suka harus menulis semuanya dua kali daripada memiliki jaminan yang tidak diketahui. (Plus ini tidak akan berperilaku berbeda berdasarkan apakah sesuatu sedang dievaluasi atau tidak.)

Bisakah Anda mendeklarasikan const dalam const fn untuk menjamin evaluasi const (untuk const fn rekursif)? Atau apakah Anda harus melalui obat generik const? Dll.

@SoniEx2 sebagai contoh bagaimana contoh Anda harus ditulis untuk memanfaatkan const fn dan berubah menjadi kesalahan waktu kompilasi jika salah satu fungsi menjadi non- const :

fn thing() {
    const x: u32 = const_fn_returns_value();
    const y: u32 = const_fn_with_1_arg(x);
}

(contoh lari penuh di taman bermain)

Sedikit kurang ergonomis karena tidak ada tipe inferensi, tapi siapa tahu, itu bisa berubah di masa depan.

daripada memiliki jaminan yang tidak diketahui.

maukah Anda berbaik hati dan memberikan beberapa contoh di mana menurut Anda ada sesuatu yang tidak jelas?

Bisakah Anda mendeklarasikan const dalam const fn untuk menjamin evaluasi const (untuk const fn rekursif)? Atau apakah Anda harus melalui obat generik const? Dll.

Maksud dari const fn bukanlah untuk secara ajaib mengevaluasi berbagai hal pada waktu kompilasi. Itu untuk dapat mengevaluasi berbagai hal pada waktu kompilasi.

Mengevaluasi hal-hal secara ajaib pada waktu kompilasi sudah terjadi sejak rustc didasarkan pada llvm. Jadi... tepatnya ketika itu berhenti diimplementasikan di ocaml. Saya tidak berpikir ada yang ingin menghapus propagasi konstan dari rustc.

const fn tidak mempengaruhi propagasi konstan dengan cara apapun. Jika Anda memiliki fungsi yang secara tidak sengaja dapat disebarkan const, dan llvm melakukannya, dan Anda mengubah fungsi itu sedemikian rupa sehingga const tidak dapat disebarkan lagi, llvm akan berhenti melakukannya. Ini sepenuhnya independen dari melampirkan const ke suatu fungsi. Untuk llvm tidak ada perbedaan antara const fn dan fn .

Pada saat yang sama rustc tidak mengubah perilakunya sama sekali ketika Anda melampirkan const ke fn (dengan asumsi fungsinya adalah const fn yang valid dan dengan demikian masih dikompilasi setelah melakukannya). Ini hanya memungkinkan Anda untuk memanggil fungsi ini dalam konstanta mulai sekarang .

Saya tidak memikirkan LLVM, saya memikirkan karat. LLVM tidak masalah bagi saya di sini.

@SoniEx2

Const fn dievaluasi pada waktu kompilasi jika memungkinkan.

Ini tidak benar. const fn , ketika dipanggil dalam konteks tertentu, AKAN dievaluasi pada waktu kompilasi. Jika itu tidak memungkinkan, ada kesalahan yang sulit. Dalam semua konteks lain, bagian const tidak penting sama sekali.

Contoh konteks yang memerlukan const fn adalah panjang array. Anda dapat menulis [i32; 15] . Anda juga dapat menulis [i32; 3+4] karena kompiler dapat menghitung 7 . Anda tidak dapat menulis [i32; read_something_from_network()] , karena bagaimana itu masuk akal? Dengan proposal ini, Anda BISA menulis [i32; foo(15)] jika foo adalah const fn , yang memastikan itu lebih seperti penambahan dan kurang seperti mengakses jaringan.

Ini sama sekali bukan tentang kode yang mungkin atau akan berjalan ketika program berjalan. Tidak ada "mungkin evaluasi pada waktu kompilasi". Yang ada hanyalah "harus mengevaluasi pada waktu kompilasi atau membatalkan kompilasi".

Silakan baca juga RFC: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md

Alih-alih memiliki anotasi const fn , bagaimana jika itu adalah properti yang disimpulkan? Itu tidak akan eksplisit dalam kode sumber, tetapi dapat secara otomatis diberi label seperti itu dalam dokumentasi yang dibuat secara otomatis. Ini akan memungkinkan perluasan pada akhirnya dari apa yang dianggap const , tanpa penulis perpustakaan perlu mengubah kode mereka. Pada awalnya, inferensi dapat dibatasi pada const fn yang saat ini didukung (fungsi deterministik murni tanpa ikatan let?).

Di bawah pendekatan ini, evaluasi akan terjadi pada waktu kompilasi jika hasilnya terikat ke variabel const , dan pada saat run-time sebaliknya. Ini tampaknya lebih diinginkan, karena memberi pemanggil (bukan yang dipanggil) kontrol ketika fungsi dievaluasi.

Hal ini telah dibahas cukup menyeluruh sudah. Kelemahan dari pendekatan itu adalah membuatnya lebih mudah untuk secara tidak sengaja pergi ke arah lain - seseorang mungkin menggunakan fungsi perpustakaan dalam konteks const , tetapi kemudian penulis perpustakaan mungkin membuatnya tidak lagi const tanpa menyadarinya.

Hmm, ya itu masalahnya..

Sunting: Satu-satunya solusi yang dapat saya pikirkan, tanpa pergi ke const fn , adalah memiliki anotasi opt-out, sehingga penulis perpustakaan dapat berhak untuk memecahkan const ness. Saya tidak yakin itu lebih baik daripada menaburkan const fn di mana-mana. Satu-satunya manfaat nyata adalah adopsi yang lebih cepat dari definisi perluasan const .

Sunting 2: Saya kira itu akan merusak kompatibilitas ke belakang, jadi ini bukan starter. Maaf untuk jalur samping.

Jadi ... diskusi telah mereda. Mari kita rangkum:

komentar rfcbot adalah https://github.com/rust-lang/rust/issues/24111#issuecomment -376649804

Kekhawatiran saat ini

  • Ini bukan prioritas, dan rilisan 2018 sudah cukup untuk kami
  • Kami mulai menandai semuanya const , yang mengganggu
  • Apakah ini desain yang ingin kita komitmenkan?

Saya tidak bisa benar-benar berbicara tentang hal prioritas + kekhawatiran kejatuhan, kecuali bahwa const fn telah dipanggang di malam hari untuk waktu yang lama

Dua poin lainnya terkait erat. Desain @eddyb (https://github.com/rust-lang/rust/issues/24111#issuecomment-376829588 seperti yang saya pahami) adalah tidak memiliki const fn , tetapi memiliki #[const] atribut yang dapat Anda tampar ke barang-barang:

#[const]
mod foo {
    pub fn square(i: i32) -> i32 { i * i }
}
#[const]
fn bar(s: &str) -> &str i{ s }
#[const]
fn boo() -> fn(u32) -> u32 { meh }
fn meh(u: u32) -> u32 { u + 1 }

dan itu secara rekursif masuk ke apa pun yang ditandai dengannya, jadi fungsi apa pun di dalam modul #[const] adalah #[const] fn . Fungsi yang dideklarasikan di dalam #[const] fn juga merupakan #[const] fn .

Ini mengurangi jumlah anotasi yang diperlukan, karena beberapa peti hanya akan memasukkan #![const] ke dalam lib.rs dan selesai.

Masalah yang saya lihat dengan desain itu (tetapi masalah itu juga sering ada di const fn ):

  • mungkin memerlukan dukungan opt-out, karena Anda mungkin ingin dapat mendeklarasikan beberapa fungsi non-const jauh di dalam pohon modul #[const] .
  • apakah pointer fungsi ke #[const] fn dalam posisi argumen/tipe pengembalian harus #[const] fn ?

    • sekali lagi, penyisihan akan diperlukan

Kami memang perlu memikirkan hal-hal ini, jadi kami tidak merancang sistem yang tidak kompatibel dengan versi mendatang di mana kami ingin dapat memanggil fungsi melalui pointer fungsi selama evaluasi konstan.

Perhatikan bahwa saya tidak mengusulkan desain tertentu, tetapi hanya mencantumkan beberapa arah yang masuk akal yang diketahui.
Ide aslinya adalah atribut "expose body of function" yang digeneralisasi, tidak terbatas pada const , tetapi ada banyak kemungkinan variasi, dan beberapa di antaranya bahkan mungkin bagus .

EDIT : (tidak ingin melupakan ini) @solson menunjukkan kepada saya bagaimana Lean memiliki atribut seperti @pattern yang secara otomatis menurunkan berbagai hal dari tubuh suatu fungsi.

@oli-obk Saya pikir kita tidak harus pergi dengan atribut, karena unsafe tidak menggunakan atribut.
Juga, async saat ini juga tidak. Dan jika kita memperkenalkan try fn yang diberikan try { .. } blok, maka kita memiliki hal lain yang tidak berdasarkan atribut. Saya pikir kita harus mencoba untuk tetap sekonsisten mungkin hal-hal yang seperti efek wrt. menggunakan atribut atau tidak. #[target_feature(..)] memang membuat konsistensi keseluruhan berkerut.

PS: Anda bisa menggunakan const mod { .. } untuk mendapatkan efek yang sama seperti #![const] kurang lebih. Ini juga dapat berlaku untuk try mod , async mod , unsafe mod .

Saya akan selalu condong ke arah melakukan hal-hal dengan tipe khusus.

struct SizeOf<T>;

impl<T> SizeOf<T> {
    const intrisic Result: usize;
}

lebih mudah untuk mempelajari tipe baru menggunakan sintaks yang ada daripada mempelajari konsep baru dengan sintaks baru.

dan kami nantinya dapat mendukung sistem tipe saat runtime.

fn sq(v: i32) -> i32 {
    Square<v>::Result
}

jenis pada waktu kompilasi, generik const baik pada waktu kompilasi atau saat runtime.

Jadi... Saya menyarankan untuk mengabaikan fakta bahwa mungkin ada desain yang mungkin lebih baik di luar sana, karena kami memiliki desain yang

  1. Mudah untuk alasan tentang
  2. Teruskan kompatibel dengan desain yang lebih permisif
  3. Secara historis telah digunakan dalam kode yang tidak stabil untuk sukses besar, dengan keluhan utama tidak cukup fitur.
  4. Dapat linted untuk menyarankan untuk menambahkan anotasi ke fungsi yang belum dianotasi yang memiliki isi yang memungkinkan menambahkannya.
  5. Saya pribadi, seperti yang diposting dalam permintaan penggabungan, melihat sebagai satu-satunya desain yang dapat kami stabilkan tanpa periode uji coba multi-tahun lagi.
  6. Memungkinkan kode untuk dibagikan antara waktu proses dan kode evaluasi konstan, alih-alih memerlukan kode khusus untuk masing-masing

dan kami nantinya dapat mendukung sistem tipe saat runtime.

Itu tergantung pengetikan, yang jauh , saat memanggil const fn saat runtime berfungsi hari ini dengan baik.

@oli-obk Bagaimana dengan sifat? Saya tidak ingin menstabilkan const fn tanpa gagasan tentang apa yang akan kita lakukan untuk metode sifat yang const fn hanya dalam beberapa sifat impl s.

@eddyb sepertinya saya harus mempercepat penulisan batasan dan metode const baru. :)

@Centril Maksud saya adalah bahwa proposal atribut (apakah menggunakan kata kunci atau tidak) akan menghasilkan pendekatan yang jauh lebih berbeda untuk menangani metode sifat, dan kami harus membandingkannya.
Pendekatan const fn saat ini mungkin tampak sederhana dan dapat diperluas, tetapi tidak ketika benar-benar diperluas.

const generik + const generik:

intrinsic const SizeOf<T>: usize;

const Pow<const V: usize>: usize = V*V;

@eddyb Saya memiliki berbagai solusi dalam pikiran yang sepenuhnya kompatibel dengan desain const fn. Aku akan menulisnya.

Woah, saya baru melihat sudah dua tahun yang telah dimulai. Apakah ada perkiraan tanggal stabilisasi? Saya memiliki peti yang hampir tersedia di stable karena menunggu ekstensi ini distabilkan. :)

@rfcbot memperhatikan runtime-pointer-addresses

Pada masalah yang berbeda, pertanyaan apakah kami ingin transparansi referensial dari const fn muncul, dan masalah alamat pointer mentah yang digunakan sebagai oracle non-determinisme muncul: https://github.com/rust- lang/rust/issues/49146#issuecomment -386727325. Ada solusi yang diuraikan di sana, tetapi ini melibatkan pembuatan beberapa operasi pointer mentah unsafe (tidak yakin berapa banyak dari mereka yang diizinkan hari ini), sebelum stabilisasi.

@eddyb Bukankah E0018 juga berlaku untuk const fn s?

Cara C adalah bahwa pointer objek diperbolehkan semua menjadi 0 kecuali relatif (yaitu di dalam objek) dan dilacak saat runtime entah bagaimana.

Saya tidak yakin apakah rust mendukung aturan aliasing C.

@sgrif Banyak kesalahan yang dipancarkan tentang konstanta akan hilang cepat atau lambat - miri tidak peduli jenis nilai yang dilihat, lokasi abstrak yang ada di nilai usize masih merupakan lokasi abstrak (dan melemparkannya ke pointer memberi Anda kembali pointer asli).

Saya baru saja memeriksa dan untuk saat ini , keduanya memberikan pointer ke bilangan bulat, dan operator perbandingan antar pointer, dilarang dalam konteks konstan. Namun, ini hanya apa yang kami pikirkan , saya masih takut.

@eddyb Cukup adil. Namun, saya berharap bahwa kekhawatiran Anda mengenai const fn telah mencapai const blok hari ini.

@sgrif Perbedaannya adalah const (bahkan terkait const yang bergantung pada parameter tipe generik) dievaluasi sepenuhnya pada waktu kompilasi, di bawah miri, sementara const fn adalah non- const fn untuk panggilan waktu proses.
Jadi, jika kita benar- benar menginginkan transparansi referensial, kita perlu memastikan bahwa kita tidak mengizinkan (setidaknya dalam kode aman) hal-hal yang dapat menyebabkan non-determinisme runtime bahkan jika itu baik-baik saja di bawah miri .
Yang mungkin berarti mendapatkan bit float juga merupakan masalah, karena misalnya muatan NaN.

Hal-hal yang harus Anda pertimbangkan untuk dilakukan di miri:

Semua pointer adalah 0 kecuali relatif. Misalnya:

#[repr(C)]
struct X {
    a: usize,
    b: u8,
}
let x = X { a: 1, b: 2 };
let y: usize = 3;
assert_eq!(&x as *const _ as usize, 0);
assert_eq!(&x.a as *const _ as usize, 0);
assert_eq!(&x.b as *const _ as usize, 8);
assert_eq!(&y as *const _ as usize, 0);

Kemudian Anda melacak mereka di evaluasi miri. Beberapa hal akan menjadi UB, seperti beralih dari penunjuk ke penggunaan kembali ke penunjuk, tetapi itu mudah dilarang (larang penggunaan konversi penunjuk. karena Anda sudah melacak penunjuk saat runtime/waktu evaluasi).

Adapun bit float, normalisasi NaN?

Saya pikir keduanya akan membuat semuanya menjadi deterministik.

Yang mungkin berarti mendapatkan bit float juga merupakan masalah, karena misalnya muatan NaN.

Saya pikir untuk transparansi referensial penuh, kita harus membuat semua operasi float tidak aman.

determinisme floating point itu sulit

Poin terpenting di sini adalah bahwa pengoptimal LLVM dapat dan memang mengubah urutan operasi float serta ~melakukan~ operasi sekering yang memiliki opcode gabungan untuknya. Perubahan ini memang mempengaruhi hasil operasi, meskipun perbedaan sebenarnya hanya sedikit. Ini memengaruhi transparansi referensial karena miri mengeksekusi mir yang tidak dioptimalkan-llvm sementara target mengeksekusi kode asli yang dioptimalkan-llvm-dan-mungkin-diatur ulang-dan-oleh karena itu-memiliki-semantik yang berbeda-daripada kode asli.

Saya akan menyetujui fitur const fn pertama yang stabil tanpa float untuk saat ini sampai ada keputusan tentang betapa pentingnya transparansi referensial, tetapi tidak ingin const fn diperlambat atau diblokir oleh diskusi itu.

Poin terpenting di sini adalah bahwa pengoptimal LLVM dapat dan memang mengubah urutan operasi float serta melakukan operasi sekering yang memiliki opcode gabungan untuknya. Perubahan ini memang mempengaruhi hasil operasi, meskipun perbedaan sebenarnya hanya sedikit.

Operasi sekering (saya berasumsi Anda merujuk ke mul-add) tidak diperbolehkan/tidak dilakukan tanpa tanda matematika cepat justru karena itu mengubah pembulatan hasil. LLVM sangat berhati-hati untuk mempertahankan semantik yang tepat dari operasi titik mengambang saat menargetkan perangkat keras yang sesuai dengan IEEE, dalam batas yang ditetapkan oleh "lingkungan titik mengambang default".

Dua hal yang LLVM tidak coba pertahankan adalah muatan NaN dan pensinyalan NaN, karena keduanya tidak dapat diamati di lingkungan fp default -- hanya dengan memeriksa bit float, yang oleh karena itu kita perlu melarangnya. (Dan bahkan jika LLVM lebih berhati-hati tentang itu, perangkat keras juga bervariasi dalam perlakuannya terhadap muatan NaN, jadi Anda tidak dapat menyematkan ini di LLVM.)

Kasus besar lainnya yang saya tahu di mana keputusan kompiler dapat membuat perbedaan untuk hasil floating point adalah lokasi tumpahan dan memuat ulang dalam kode x87 (pra-SSE). Dan ini sebagian besar merupakan masalah karena x87 secara default dibulatkan ke 80 bit untuk hasil antara dan dibulatkan ke 32 atau 64 bit di toko. Mengatur mode pembulatan dengan benar sebelum setiap instruksi FPU untuk mencapai hasil yang dibulatkan dengan benar dimungkinkan, tetapi itu tidak terlalu praktis dan karenanya (saya percaya) LLVM tidak mendukungnya.

Pada Transparansi Referensi penuh

Pandangan saya adalah bahwa kita harus pergi dengan transparansi referensial penuh karena sesuai dengan pesan Rust keseluruhan memilih keselamatan / kebenaran atas kenyamanan / kelengkapan .

Transparansi referensial menambahkan banyak manfaat penalaran seperti memungkinkan penalaran persamaan (sampai dasar, tetapi penalaran cepat dan longgar secara moral benar ).

Namun, tentu saja ada kekurangan wrt. kehilangan kelengkapan wrt. KKP. Maksud saya, mekanisme const fn yang transparan secara referensi tidak akan dapat mengevaluasi sebanyak yang dapat dilakukan oleh skema const fn yang tidak transparan pada waktu kompilasi. Dari mereka yang mengusulkan untuk tidak berpegang teguh pada transparansi referensial, saya akan meminta mereka memberikan sebanyak mungkin kasus penggunaan konkret yang mereka dapat terhadap proposisi ini sehingga kami dapat mengevaluasi trade-off.

Oke, sepertinya Anda benar tentang poin LLVM, sepertinya memang menghindari operasi yang salah secara komputasi kecuali Anda mengaktifkan mode matematika cepat.

Namun, masih ada banyak status yang bergantung pada operasi floating point seperti presisi internal. Apakah evaluator float CTFE mengetahui nilai presisi internal saat runtime?

Juga, selama menumpahkan nilai ke memori, kami mengonversi nilai internal ke format ieee754 dan dengan demikian mengubah presisi. Ini dapat mempengaruhi hasil juga, dan algoritma dengan compiler melakukan spilling tidak ditentukan, bukan?

@est31 Perhatikan bahwa saya telah berasumsi bahwa kami tidak peduli jika waktu kompilasi dan perilaku runtime berbeda, hanya saja panggilan berulang (dengan grafik objek beku) konsisten dan bebas efek samping global.

Jadi, jika kita benar-benar menginginkan transparansi referensial, kita perlu memastikan bahwa kita tidak mengizinkan (setidaknya dalam kode aman) hal-hal yang dapat menyebabkan non-determinisme runtime bahkan jika itu baik-baik saja di bawah miri.

Jadi tujuannya di sini adalah untuk menjamin bahwa const fn adalah deterministik dan bebas efek samping pada saat run-time bahkan jika miri akan melakukan kesalahan selama eksekusi , setidaknya jika const fn sepenuhnya aman kode? Saya tidak pernah menganggap itu penting, TBH. Ini adalah persyaratan yang cukup kuat, dan memang kita setidaknya harus membuat ptr-to-int dan float-to-bits tidak aman, yang akan sulit untuk dijelaskan. OTOH, kita juga harus membuat perbandingan pointer mentah tidak aman dan saya akan senang tentang itu :D

Apa motivasi untuk ini? Sepertinya kami mencoba untuk memperkenalkan kembali kemurnian dan sistem efek, yang pernah dimiliki dan hilang oleh Rust, mungkin karena mereka tidak membawa bobotnya (EDIT: atau karena itu tidak cukup fleksibel untuk melakukan semua yang berbeda hal-hal yang ingin digunakan orang, lihat https://mail.mozilla.org/pipermail/rust-dev/2013-April/003926.html).

ptr-to-int aman jika mengkonversi dari awal objek, dan Anda mengizinkan int-to-ptr gagal. itu akan menjadi 100% deterministik. dan setiap panggilan akan menghasilkan hasil yang sama.

ptr-ke-int aman, int-ke-ptr tidak aman. ptr-to-int harus diizinkan hasil "tak terduga" dalam evaluasi const .

dan Anda harus benar-benar menggunakan kanonikalisasi NaN dalam juru bahasa/VM. (contohnya adalah LuaJIT, yang membuat semua hasil NaN menjadi NaN kanonik, dan setiap NaN lainnya adalah NaN yang dikemas)

ptr-to-int aman jika mengkonversi dari awal objek, dan Anda mengizinkan int-to-ptr gagal. itu akan menjadi 100% deterministik. dan setiap panggilan akan menghasilkan hasil yang sama.

Bagaimana Anda menyarankan agar kami menerapkan ini saat run-time? (&mut *Box::new(...)) as usize adalah fungsi non-deterministik saat ini dan seharusnya semuanya const fn . Jadi, bagaimana Anda menyarankan agar kami memastikan bahwa itu "secara referensial transpent saat run-time" dalam arti selalu mengembalikan nilai yang sama?

Box::new harus mengembalikan pointer terlacak di VM.

Konversi akan menghasilkan 0, pada waktu kompilasi (yaitu dalam evaluasi const). Dalam evaluasi non-const, itu akan mengembalikan apa pun.

Pointer yang dilacak berfungsi seperti ini:

Anda mengalokasikan pointer, dan Anda menetapkannya. Saat Anda menetapkannya, VM menetapkan nilai penunjuk ke 0, tetapi mengambil alamat memori penunjuk, dan melampirkannya ke tabel pencarian. Saat Anda menggunakan penunjuk, Anda menelusuri tabel pencarian. Ketika Anda mendapatkan nilai pointer (yaitu byte yang membentuknya), Anda mendapatkan 0. Dengan asumsi itu adalah pointer dasar dari suatu objek.

Box::new harus mengembalikan pointer terlacak di VM.

Kita berbicara tentang perilaku run-time di sini. Seperti, apa yang terjadi ketika ini dieksekusi dalam biner. Tidak ada VM.

miri sudah bisa menangani semua ini dengan baik dan sepenuhnya deterministik.


Juga, kembali ke kekhawatiran tentang const fn dan runtime determinisim: Jika kita menginginkan itu (yang saya tidak yakin kita lakukan, masih menunggu beberapa untuk menjelaskan kepada saya mengapa kita peduli :D ), kita bisa mendeklarasikan ptr-to-int dan float-to-bits menjadi operasi non-const. Apakah ada masalah dengan itu?

@RalfJung Saya menautkan https://github.com/rust-lang/rust/issues/49146#issuecomment -386727325 sudah ada dalam komentar yang mengkhawatirkan, mungkin itu hilang di bawah pesan lain - itu termasuk deskripsi solusi yang diusulkan @Centril ( buat operasi unsafe dan daftar putih beberapa "penggunaan yang benar", misalnya offset_of untuk ptr-to-int dan float-to-bit yang dinormalisasi NaN).

@eddyb yakin, ada berbagai solusi; apa yang saya minta adalah motivasi. Saya juga belum menemukannya di sana.

Juga, mengutip diri saya sendiri dari IRC: Saya pikir kita bahkan dapat dengan tepat berargumen bahwa CTFE adalah deterministik, bahkan ketika pemeran ptr-to-int diperbolehkan. Masih membutuhkan kode non-CTFE (misalnya mengekstraksi bit dari ptr cast ke int) untuk benar-benar mengamati non-determinisme.

const fn thing() -> usize {
    (*Box::new(0)) as *const _ as usize
}

const X: usize = thing();
const Y: usize = thing();

adalah X == Y?

dengan saran saya, X == Y.

@SoniEx2 yang merusak foo as *const _ as usize as *const _ yang benar-benar noop sekarang

motivasi wrt:
Saya pribadi tidak melihat masalah dengan perilaku runtime yang berbeda dari perilaku waktu kompilasi atau bahkan const fn menjadi nondeterministik saat runtime. Keamanan bukanlah masalah di sini, jadi tidak aman sepenuhnya merupakan kata kunci yang salah. Ini hanyalah perilaku mengejutkan yang benar-benar kami tangkap pada evaluasi waktu kompilasi. Anda sudah dapat melakukan ini dalam fungsi normal, jadi tidak ada kejutan di sana. Const fn adalah tentang menandai fungsi yang ada sebagai dapat dievaluasi pada waktu kompilasi tanpa mempengaruhi runtime. Ini bukan tentang kemurnian, setidaknya itulah getaran yang saya dapatkan ketika ppl berbicara tentang "neraka murni" (saya tidak ada, jadi saya mungkin salah menafsirkan)

@SoniEx2 Ya, X == Y selalu.

Namun, jika Anda memiliki:

const fn oracle() -> bool { let x = 0; let y = &x as *const _ as usize; even(y) }

fn main() {
    assert_eq!(oracle(), oracle());
}

Kemudian main mungkin panik saat runtime.

@RalfJung

atau karena tidak cukup fleksibel untuk melakukan semua hal berbeda yang orang ingin gunakan untuk [..]

Apa saja hal-hal itu? Sulit untuk mengevaluasi ini tanpa konkret.

Saya pikir kita bahkan dapat dengan tepat berargumen bahwa CTFE bersifat deterministik, bahkan ketika pemeran ptr-to-int diperbolehkan.
[...]
Masih membutuhkan kode non-CTFE (misalnya mengekstraksi bit dari ptr cast ke int) untuk benar-benar mengamati non-determinisme.

Ya, const fn masih deterministik ketika dieksekusi pada waktu kompilasi, tetapi saya tidak percaya bahwa itu deterministik saat runtime karena akan selalu ada beberapa non- const fn (hanya fn ) kode jika hasil const fn berguna untuk apa pun saat runtime, dan kemudian kode fn itu akan mengamati efek samping dari const fn yang dieksekusi.

Inti dari pemisahan antara kode murni dan tidak murni di Haskell adalah Anda dapat membuat keputusan "apakah akan ada efek samping" sebagai keputusan lokal di mana Anda tidak perlu memikirkan kemungkinan efek samping secara global. Yaitu, diberikan:

reverse :: [a] -> [a]
reverse []       = []
reverse (x : xs) = reverse xs ++ [x]

putStrLn :: String -> IO ()
getLine :: IO String

main :: IO ()
main = do
    line <- getLine
    let revLine = reverse line
    putStrLn revLine

Anda tahu bahwa hasil dari revLine di let revLine = reverse line hanya dapat bergantung pada status yang diteruskan ke reverse yaitu line . Ini memberikan manfaat penalaran dan pemisahan yang bersih.

Saya ingin tahu seperti apa sistem efek saat itu ... Saya pikir kita perlu satu yang diberikan const fn , async fn , dll. Lagi pula untuk melakukannya dengan bersih dan mendapatkan penggunaan kembali kode (sesuatu https:// github.com/rust-lang/rfcs/pull/2237 tetapi dengan beberapa perubahan ...) dan parametrik sepertinya ide yang bagus untuk itu.

Dalam kata-kata abadi Phil Wadler dan Conor McBride:

Haruskah saya menjadi murni atau tidak murni?
—Philip Wadler [60]

Kami mengatakan 'Ya.': kemurnian adalah pilihan yang dibuat secara lokal

https://arxiv.org/pdf/1611.09259.pdf

@oli-obk

Saya tidak iri pada orang yang harus mendokumentasikan perilaku mengejutkan ini bahwa hasil eksekusi dapat berbeda untuk const fn jika dieksekusi pada saat runtime atau pada waktu kompilasi dengan argumen yang sama.

Sebagai opsi konservatif, saya mengusulkan agar kita menunda keputusan tentang transparansi / kemurnian referensial dengan menstabilkan const fn sebagai transparan referensial, menjadikannya unsafe (atau non- const ) untuk dilanggar itu, tapi kami sebenarnya tidak menjamin transparansi referensial, jadi orang juga tidak bisa berasumsi.

Di lain waktu, ketika kami telah memperoleh pengalaman, kami kemudian dapat membuat keputusan.
Mendapatkan pengalaman termasuk orang yang mencoba memanfaatkan non-determinisme tetapi gagal, dan kemudian melaporkannya dan memberi tahu kami tentang kasus penggunaan mereka.

Ya tapi itu bukan evaluasi const.

Mengonversi int ke ptr di const sepertinya bukan kerugian besar. Mendapatkan offset lapangan tampaknya lebih penting, dan itulah yang saya coba pertahankan.

Saya tidak ingin mengubah perilaku secara diam-diam saat menambahkan const ke fungsi @SoniEx2 dan pada dasarnya itulah yang akan dilakukan saran Anda.

@centril Saya setuju, mari kita lakukan hal yang konservatif sekarang. jadi kami tidak membuat ini tidak aman, tetapi tidak stabil. dengan cara ini libstd dapat menggunakannya, tetapi nanti kita dapat memutuskan detailnya.

Satu masalah yang kami miliki adalah bahwa pengguna selalu dapat membuat bata analisis apa pun yang kami lakukan dengan memperkenalkan serikat pekerja dan melakukan beberapa konversi mewah, jadi kami harus membuat konversi yang menghindari UB sehingga kami diizinkan untuk mengubahnya nanti.

itu akan tetap sama saat runtime, const hanya akan mengubah hal-hal pada waktu const, yang ... tidak akan mungkin dilakukan tanpa const.

tidak masalah jika const fns menjadi subbahasa dengan aturan aliasing pointer yang berbeda.

@Centril @est31 Saya tidak yakin apa hubungannya Chlorotrifluoroethylene dengan apa pun (dapatkah kita menghindari penggunaan banyak akronim tanpa mendefinisikannya?)

@sgrif CTFE (evaluasi fungsi waktu kompilasi) telah lama digunakan untuk Rust (termasuk di bagian utas ini yang berusia bertahun-tahun). Itu salah satu istilah yang harus didefinisikan di suatu tempat sentral.

@sgrif lol maaf sering kali saya adalah orang yang bertanya-tanya "kata-kata aneh mana yang mereka gunakan sekarang?". Saya pikir saya sedang berbicara dengan @eddyb di IRC ketika dia menyebutkan "CTFE" dan saya harus bertanya kepadanya apa artinya :p.

@eddyb ctrl+f + CTFE di halaman ini tidak mengarah pada apa pun yang mendefinisikannya. Either way, saya kebanyakan hanya berasumsi bahwa kita tidak ingin memaksa orang untuk melakukan banyak penggalian untuk mengambil bagian dalam diskusi di sini. "Anda seharusnya sudah tahu apa artinya ini" adalah IMO yang cukup eksklusif.

@Centril @est31 terima kasih :heart:

Jadi... ada pemikiran tentang

Satu masalah yang kami miliki adalah bahwa pengguna selalu dapat membuat bata analisis apa pun yang kami lakukan dengan memperkenalkan serikat pekerja dan melakukan beberapa konversi mewah, jadi kami harus membuat konversi yang menghindari UB sehingga kami diizinkan untuk mengubahnya nanti.

Haruskah akses bidang serikat pekerja di dalam const fn juga tidak stabil?

ctrl+f + CTFE pada halaman ini tidak mengarah pada apa pun yang mendefinisikannya.

Maaf, yang saya maksud adalah, penggunaannya dalam desain & pengembangan Rust mendahului masalah pra-1.0 ini.
Seharusnya ada dokumen pusat untuk istilah-istilah seperti itu, tetapi sayangnya, glosarium referensinya sangat kurang.
HRTB dan NLL adalah dua contoh akronim lain yang lebih baru tetapi juga tidak dijelaskan sebaris.

Saya tidak ingin diskusi menjadi eksklusif, tetapi saya rasa tidak adil untuk meminta @Centril atau @est31 untuk mendefinisikan CTFE, karena keduanya tidak memperkenalkan akronim sejak awal.
Ada solusi yang sayangnya tidak selalu bekerja di GitHub, yang pernah saya lihat di subreddit tertentu, di mana bot akan membuat komentar tingkat atas dan terus memperbaruinya , dengan daftar perluasan untuk semua akronim yang umum digunakan dari subreddit yang muncul dalam diskusi.

Juga, saya ingin tahu apakah saya berada di gelembung Google, karena dua artikel wikipedia untuk CTFE ("Chlorotrifluoroethylene" dan "Eksekusi fungsi waktu kompilasi") adalah dua hasil pertama untuk saya. Bahkan kemudian, yang pertama dimulai dengan "Untuk fitur kompiler, lihat eksekusi fungsi waktu kompilasi.".

( Saya meletakkan tautan wiki CTFE di atas; sekarang bisakah kita kembali ke const fn s? :P)

Bisakah seseorang menambahkan CTFE ke glosarium panduan rustc (saya menggunakan ponsel)?

Juga, saya pikir kita harus menstabilkan minimum dan melihat apa yang sering dilakukan orang dalam praktik, yang mengirim ke pendekatan saat ini dengan const if dan ekspresi pencocokan.

@Centril

Saya tidak iri pada orang yang harus mendokumentasikan perilaku mengejutkan ini bahwa hasil eksekusi dapat berbeda untuk const fn jika dieksekusi saat runtime atau pada waktu kompilasi dengan argumen yang sama.

even yang Anda tulis di atas akan gagal jika dieksekusi pada waktu CTFE karena memeriksa bit pointer. (Meskipun sebenarnya kita dapat secara deterministik mengatakan bahwa ini bahkan karena penyelarasan, dan jika uji kemerataan dilakukan melalui operasi bit, "miri penuh" akan melakukannya dengan benar. Tapi mari kita asumsikan Anda menguji seluruh byte yang paling tidak signifikan, bukan hanya byte bagian terakhir.)
Kasus yang kita bicarakan di sini adalah fungsi yang error dengan kesalahan interpreter pada waktu kompilasi, tetapi berhasil pada saat run-time. Perbedaannya adalah apakah fungsi tersebut bahkan menyelesaikan eksekusi. Saya rasa itu tidak sulit untuk dijelaskan.

Saya setuju bahwa jika CTFE berhasil dengan result , maka versi run-time dari fungsi tersebut juga harus dijamin berhasil dengan hasil yang sama. Tapi itu jaminan yang jauh lebih lemah daripada yang kita bicarakan di sini, bukan?

@oli-obk

Saya setuju, mari kita lakukan hal yang konservatif sekarang. jadi kami tidak membuat ini tidak aman, tetapi tidak stabil. dengan cara ini libstd dapat menggunakannya, tetapi nanti kita dapat memutuskan detailnya.

Saya kehilangan konteks, apa "ini" di sini?

Saat ini, untungnya, CTFE miri langsung menolak untuk melakukan apa pun dengan nilai pointer -- aritmatika, perbandingan, semuanya salah. Ini adalah pemeriksaan yang dilakukan pada waktu CTFE berdasarkan nilai yang sebenarnya digunakan dalam perhitungan, tidak dapat dielakkan oleh serikat pekerja dan bagaimanapun juga kode yang diperlukan untuk melakukan aritmatika/perbandingan tidak ada . Oleh karena itu saya cukup yakin bahwa kami memenuhi jaminan yang saya nyatakan di atas.

Saya bisa membayangkan masalah jika kami meminta CTFE mengembalikan nilai pointer, tetapi bagaimana nilai pointer yang dihitung waktu kompilasi bahkan masuk akal di mana saja? Saya berasumsi kita sudah memeriksa apa pun yang dihitung miri untuk tidak mengandung nilai pointer karena kita harus mengubahnya menjadi bit?

Kita dapat dengan hati-hati menambahkan operasi ke CTFE miri, dan sebenarnya yang kita butuhkan untuk offset_of [1] @eddyb adalah pengurangan pointer. Kode itu ada di "full miri", dan hanya berhasil jika kedua pointer berada di dalam alokasi yang sama, yang cukup untuk mempertahankan jaminan di atas. Apa yang tidak akan berhasil adalah assert yang ditambahkan @eddyb sebagai pengaman.
Kami juga dapat mengizinkan operasi bit pada nilai pointer jika operasi hanya memengaruhi bagian penunjuk yang disejajarkan, itu masih deterministik dan kode sebenarnya sudah ada di "miri penuh".

EDIT: [1] Untuk referensi, saya merujuk ke makronya di utas ini yang tidak dapat kami tautkan karena ditandai di luar topik, jadi inilah salinannya:

macro_rules! offset_of {
    ($Struct:path, $field:ident) => ({
        // Using a separate function to minimize unhygienic hazards
        // (e.g. unsafety of #[repr(packed)] field borrows).
        // Uncomment `const` when `const fn`s can juggle pointers.
        /*const*/ fn offset() -> usize {
            let u = $crate::mem::MaybeUninit::<$Struct>::uninit();
            // Use pattern-matching to avoid accidentally going through Deref.
            let &$Struct { $field: ref f, .. } = unsafe { &*u.as_ptr() };
            let o = (f as *const _ as usize).wrapping_sub(&u as *const _ as usize);
            // Triple check that we are within `u` still.
            assert!((0..=$crate::mem::size_of_val(&u)).contains(&o));
            o
        }
        offset()
    })
}

EDIT2: Sebenarnya, dia juga mempostingnya di sini .

"ini" adalah float -> konversi bit dan pointer -> gunakan konversi

Saya setuju bahwa jika CTFE berhasil dengan suatu hasil, maka versi run-time dari fungsi tersebut juga harus dijamin berhasil dengan hasil yang sama. Tapi itu jaminan yang jauh lebih lemah daripada yang kita bicarakan di sini, bukan?

Jadi fungsi yang dipanggil dengan argumen saat run time hanya dijamin kemurniannya jika benar-benar berakhir jika dievaluasi pada waktu kompilasi dengan argumen yang sama?

Itu akan membuat hidup kita sejuta kali lebih mudah, terutama karena saya tidak melihat cara untuk mencegah nondeterminisme tanpa meninggalkan celah atau melumpuhkan const fn dengan cara melanggar-perubahan.

Jadi fungsi yang dipanggil dengan argumen saat run time hanya dijamin kemurniannya jika benar-benar berakhir jika dievaluasi pada waktu kompilasi dengan argumen yang sama?

Ya, itulah yang saya usulkan.


Memikirkannya lagi (dan membaca balasan @ oli-obk yang muncul ketika saya menulis ini), saya merasa bahwa yang Anda inginkan adalah jaminan tambahan di sepanjang baris "a safe const fn won't error at Waktu CTFE (selain panik) saat dipanggil dengan argumen yang valid". Semacam jaminan "keamanan const". Bersama dengan jaminan yang saya nyatakan di atas tentang CTFE yang sukses bertepatan dengan perilaku run-time, itu akan memberikan jaminan bahwa const fn yang aman akan deterministik pada saat run-time, karena cocok dengan eksekusi CTFE yang berhasil.

Saya setuju bahwa itu adalah jaminan yang lebih sulit untuk didapatkan. Baik atau buruk, Rust memiliki berbagai operasi aman yang tidak dapat dijamin oleh CTFE miri untuk selalu berhasil dijalankan dengan tetap mempertahankan determinisme, seperti varian dari @Centril 's oracle yang menguji byte paling tidak signifikan untuk menjadi 0. Dari perspektif dari CTFE dalam pengaturan ini, "nilai valid dari tipe usize " hanya merupakan nilai yang PrimVal::Bytes , sedangkan PrimVal::Ptr tidak boleh diizinkan [1]. Sepertinya kita memiliki sistem tipe yang sedikit berbeda dalam konteks const -- Saya tidak mengusulkan kita mengubah apa yang miri lakukan, saya mengusulkan kita mengubah apa "arti" berbagai tipe Rust ketika dilampirkan ke const fn . Sistem tipe seperti itu akan menjamin bahwa semua operasi aritmatika dan bit yang aman tidak akan salah dalam CTFE: Untuk input yang valid-CTFE, pengurangan bilangan bulat tidak akan pernah gagal dalam CTFE karena kedua sisi adalah PrimVal::Bytes . Tentu saja, ptr-to-int harus menjadi operasi yang tidak aman dalam pengaturan ini karena nilai kembaliannya bertipe usize tetapi bukan CTFE-valid usize .

Jika ini adalah jaminan yang kami pedulikan, sepertinya tidak masuk akal bagi saya untuk membuat sistem tipe lebih ketat saat memeriksa fungsi CTFE; lagi pula, kami ingin menggunakannya untuk melakukan lebih banyak hal (memeriksa "keamanan const"). Saya pikir akan sangat masuk akal, kemudian, untuk mendeklarasikan ptr-to-int melemparkan unsafe dalam konteks const , dengan alasan bahwa ini diperlukan karena konteks const membuat jaminan tambahan.

Sama seperti "keamanan runtime" normal kami dapat ditumbangkan oleh kode yang tidak aman, begitu juga "keamanan const", dan itu tidak masalah. Saya tidak melihat ada masalah dengan kode tidak aman yang masih dapat melakukan gips ptr-to-int melalui serikat pekerja -- bagaimanapun, itu adalah kode yang tidak aman . Di dunia ini, kewajiban pembuktian untuk kode tidak aman dalam konteks const lebih kuat daripada yang dalam konteks non-const; jika fungsi Anda mengembalikan bilangan bulat, Anda harus membuktikan bahwa ini akan selalu menjadi PrimVal::Bytes dan tidak pernah menjadi PrimVal::Ptr .

Makro @eddyb akan membutuhkan blok yang tidak aman, tetapi masih aman untuk digunakan karena hanya menggunakan "fitur tidak aman const" ini (ptr-untuk-menggunakan dan kemudian mengurangi hasilnya, atau hanya menggunakan pointer pengurangan intrinsik langsung) dengan cara yang dijamin tidak menimbulkan kesalahan CTFE.

Biaya dari sistem semacam itu adalah bahwa const fn safe-order yang lebih tinggi harus mengambil penutupan const fn untuk dapat menjamin bahwa mengeksekusi penutupan itu sendiri tidak akan melanggar "keamanan const". Begitulah harga untuk benar-benar mendapatkan jaminan yang layak tentang keamanan const fn .

[1] Saya sepenuhnya mengabaikan float di sini karena saya tidak tahu banyak tentang di mana non-determinisme akan muncul. Adakah yang bisa memberikan contoh kode floating point yang akan berperilaku berbeda pada waktu CTFE daripada saat run-time? Apakah cukup untuk misalnya membuat kesalahan miri jika, ketika melakukan operasi floating point, salah satu operan adalah NaN pensinyalan (untuk mendapatkan jaminan pertama, yang dari posting saya sebelumnya), dan untuk mengatakan bahwa sistem tipe CTFE tidak mengizinkan pensinyalan NaN pada tipe f32 / f64 (untuk mendapatkan "keamanan const")?

Apa yang tidak akan berhasil adalah pernyataan yang ditambahkan @eddyb sebagai pengaman.

Tentu, tetapi Anda dapat menulis ulang assert!(condition); menjadi [()][!condition as usize]; untuk saat ini.

Tentu, tetapi Anda dapat menulis ulang assert!(condition); ke [()][!kondisikan sebagai penggunaan]; untuk sekarang.

Ini bukan pernyataan yang saya pikirkan, ini adalah tes kesetaraan pointer dalam kondisi Anda. Kesetaraan pointer itu jahat dan saya lebih suka jika kita tidak mengizinkannya di CTFE.

EDIT: Sudahlah, saya baru menyadari assert menguji offset. Jadi sebenarnya itu tidak akan pernah gagal pada waktu CTFE karena jika pointer tidak berada di blok yang sama saat melakukan wrapping_sub , miri akan error.

// guess we can't have this as const fn
fn is_eq<T>(a: &T, b: &T) -> bool {
    a as *const _ == b as *const _
}

seperti yang saya katakan sebelumnya, gunakan pointer virtual di miri, daripada pointer nyata. itu dapat memberikan determinisme konstanta pada waktu konstan. jika fungsi ditulis dengan benar, perilaku runtime dan waktu kompilasi harus menghasilkan hasil yang sama terlepas dari runtime yang non-deterministik sedangkan waktu kompilasi adalah deterministik. Anda dapat memiliki perilaku deterministik di lingkungan non-deterministik jika Anda mengkodekannya.

mendapatkan bidang offset adalah deterministik. dengan pointer virtual, tetap deterministik. dengan pointer nyata, itu masih deterministik.

mendapatkan pemerataan bit ke-14 dari sebuah pointer tidak deterministik. dengan pointer virtual, itu menjadi deterministik. dengan pointer nyata, itu tidak deterministik. ini baik-baik saja, karena satu terjadi pada waktu kompilasi (lingkungan deterministik), sementara yang lain terjadi saat runtime (lingkungan non-deterministik).

const fn harus sama deterministiknya dengan lingkungan tempat mereka digunakan.

@SoniEx2

// guess we can't have this as const fn

Memang kita tidak bisa. Saya pikir saya bisa hidup dengan versi perbandingan pointer mentah yang error jika salah satu pointer saat ini tidak dapat direferensikan dalam arti berada di dalam objek yang dialokasikan (tapi bukan itu yang diimplementasikan "full miri" saat ini). Namun, itu masih akan membuat is_eq tidak "const safe" karena jika T berukuran nol, itu bisa menunjuk satu melewati akhir objek bahkan jika kita hanya mempertimbangkan kode aman.

C++ memungkinkan membandingkan pointer yang melewati ujung untuk menghasilkan hasil yang tidak pasti (pikirkan: nondeterministik). Baik C dan C++ memungkinkan membandingkan pointer yang menjuntai untuk menghasilkan hasil yang tidak pasti. Tidak jelas apa yang dijamin LLVM tetapi saya lebih suka tidak bertaruh pada jaminan yang melebihi apa yang harus mereka jamin untuk C/C++ (yang lebih lemah dari keduanya, jika berbeda). Ini adalah masalah jika kita ingin menjamin determinisme run-time untuk semua yang berhasil dijalankan di CTFE, yang menurut saya kita lakukan.

@RalfJung

Perbedaannya adalah apakah fungsi tersebut bahkan menyelesaikan eksekusi.

Pendukung Iblis: "mengembalikan " dalam satu kasus sama karena memiliki hasil yang berbeda.

Saya rasa itu tidak sulit untuk dijelaskan.

Secara pribadi saya melihatnya sebagai perilaku yang mengejutkan; Anda dapat menjelaskannya, dan saya mungkin mengerti (tetapi saya tidak mewakili ...), tetapi itu tidak sesuai dengan intuisi saya.

@oli-obk

Jadi fungsi yang dipanggil dengan argumen saat run time hanya dijamin kemurniannya jika benar-benar berakhir jika dievaluasi pada waktu kompilasi dengan argumen yang sama?

Secara pribadi, saya tidak menemukan jaminan ini cukup. Saya pikir pertama-tama kita harus melihat seberapa jauh kita bisa mendapatkan kemurnian dan hanya ketika kita tahu itu melumpuhkan dalam praktiknya kita harus bergerak menuju jaminan yang lebih lemah.

@RalfJung

yang akan memberikan jaminan bahwa aman const fn akan deterministik pada saat run-time, karena cocok dengan keberhasilan eksekusi CTFE.

OKE; Anda kehilangan saya; Saya tidak mengerti bagaimana Anda sampai pada jaminan "safe const fn is deterministic" ini dengan dua premis; bisa Anda jelaskan alasannya?

Jika ini adalah jaminan yang kami pedulikan, sepertinya tidak masuk akal bagi saya untuk membuat sistem tipe lebih ketat saat memeriksa fungsi CTFE; lagi pula, kami ingin menggunakannya untuk melakukan lebih banyak hal (memeriksa "keamanan const"). Saya pikir akan sangat masuk akal, kemudian, untuk menyatakan ptr-to-int cast tidak aman dalam konteks const, dengan alasan bahwa ini diperlukan karena konteks const membuat jaminan tambahan.

Sama seperti "keamanan runtime" normal kami dapat ditumbangkan oleh kode yang tidak aman, begitu juga "keamanan const", dan itu tidak masalah. Saya tidak melihat ada masalah dengan kode tidak aman yang masih dapat melakukan gips ptr-to-int melalui serikat pekerja -- bagaimanapun, itu adalah kode yang tidak aman. Di dunia ini, kewajiban pembuktian untuk kode tidak aman dalam konteks const lebih kuat daripada yang dalam konteks non-const; jika fungsi Anda mengembalikan bilangan bulat, Anda harus membuktikan bahwa ini akan selalu menjadi PrimVal::Bytes dan tidak pernah menjadi PrimVal::Ptr .

Paragraf ini adalah musik di telinga saya! ❤️ Ini tampaknya memastikan determinisme ("kemurnian")? dan justru hal yang saya pikirkan sebelumnya. Saya pikir keamanan const juga merupakan istilah yang hebat!

Untuk referensi di masa mendatang, izinkan saya menyebut jaminan ini "kesehatan CTFE": Jika CTFE tidak error, maka perilakunya cocok dengan run-time -- keduanya berbeda, atau keduanya selesai dengan nilai yang sama. (Saya sepenuhnya mengabaikan nilai pengembalian tingkat tinggi di sini.)

@Centril

Pendukung Iblis: "mengembalikan " dalam satu kasus sama karena memiliki hasil yang berbeda.

Nah, itu jelas masalah definisi. Saya pikir Anda memahami jaminan kesehatan CTFE yang saya jelaskan dan Anda setuju bahwa itu adalah jaminan yang kami inginkan; apakah itu semua yang kita inginkan untuk diskusi :)

OKE; Anda kehilangan saya; Saya tidak mengerti bagaimana Anda sampai pada jaminan "safe const fn is deterministic" ini dengan dua premis; bisa Anda jelaskan alasannya?

Katakanlah kita memiliki beberapa panggilan ke foo(x) di mana foo adalah fungsi safe const dan x adalah nilai const-valid (yaitu, bukan &y as *const _ as usize ). Kemudian kita tahu bahwa foo(x) akan dieksekusi di CTFE tanpa menimbulkan kesalahan, dengan keamanan const. Akibatnya, berdasarkan kesehatan CTFE, pada saat run-time foo(x) akan berperilaku sama seperti di CTFE.

Pada dasarnya saya pikir saya menguraikan jaminan Anda menjadi dua bagian - satu memastikan bahwa const fn yang aman tidak akan pernah mencoba melakukan sesuatu yang tidak didukung CTFE (seperti membaca dari stdin, atau menentukan apakah byte pointer yang paling tidak signifikan adalah 0), dan satu memastikan bahwa CTFE apa pun yang mendukung cocok dengan runtime.

Paragraf ini adalah musik di telinga saya! jantung Ini tampaknya memastikan determinisme ("kemurnian")? dan justru hal yang saya pikirkan sebelumnya. Saya pikir keamanan const juga merupakan istilah yang hebat!

Senang kamu menyukainya. :) Ini berarti saya akhirnya mengerti apa yang kita bicarakan di sini. "kemurnian" bisa berarti banyak hal yang berbeda, saya sering merasa sedikit tidak nyaman ketika istilah itu digunakan. Dan determinisme bukanlah kondisi yang cukup untuk keamanan const, kriteria yang relevan adalah apakah eksekusi di CTFE menimbulkan kesalahan. (Salah satu contoh fungsi non-const-safe deterministc adalah varian saya dari orcale Anda dikalikan dengan 0. Ini tidak boleh dilakukan bahkan dengan menggunakan kode yang tidak aman karena miri akan error saat memeriksa byte pointer, bahkan jika byte pada akhirnya tidak masalah. Ini seperti operasi yang mengekstraksi byte pointer yang paling tidak signifikan adalah "const-UB" dan karenanya tidak diizinkan bahkan dalam kode const yang tidak aman.)

ya, pointer yang melewati akhir elemen array akan menunjuk ke elemen array berikutnya, mungkin. terus? itu tidak benar-benar nondeterministik? deterministik pada waktu kompilasi adalah yang terpenting. sejauh yang saya peduli, evaluasi runtime bisa segfault untuk kelelahan tumpukan.

@RalfJung

Biaya dari sistem seperti itu adalah bahwa const fn urutan lebih tinggi yang aman harus mengambil const fn penutupan untuk dapat menjamin bahwa mengeksekusi penutupan itu sendiri tidak akan melanggar "keamanan const". Begitulah harga untuk benar-benar mendapatkan jaminan yang layak tentang keamanan const fn .

Saya percaya ini dapat sangat dikurangi untuk mendukung transformasi sebagian besar kode yang ada dengan memperkenalkan ?const di mana Anda dapat menulis fungsi tingkat tinggi yang hasilnya dapat diikat ke const jika dan hanya jika fungsi yang disediakan adalah ?const fn(T) -> U dan di mana is_const(x : T) ; Jadi kamu punya:

?const fn twice(fun: ?const fn(u8) -> u8) { fun(fun(42)) }

fn id_impure(x: u8) -> u8 { x }
const fn id_const(x: u8) -> u8 { x }
?const fn id_maybe_const(x: u8) -> u8 { x }

fn main() {
    let a = twice(id_impure); // OK!
    const b = twice(id_impure); // ERR!
    let c = twice(id_const); // OK!
    const d = twice(id_const); // OK!
    let e = twice(id_maybe_const); // OK!
    const f = twice(id_maybe_const); // OK!
}

Saya akan menulis RFC yang mengusulkan sesuatu untuk efek ini (dan lebih banyak lagi) dalam seminggu atau lebih.

@Centril pada titik ini Anda sedang mengembangkan sistem efek dengan polimorfisme efek. Saya tahu itu selalu menjadi agenda rahasia (?) Anda, hanya memberi tahu Anda bahwa itu semakin jelas.^^

@RalfJung Saya sudah mengungkapkan rahasianya di https://github.com/rust-lang/rfcs/pull/2237 tahun lalu tapi saya harus menulis ulang ;)
Cukup banyak domain publik sekarang ^,-

@SoniEx2

ya, pointer yang melewati akhir elemen array akan menunjuk ke elemen array berikutnya, mungkin. terus? itu tidak benar-benar nondeterministik? deterministik pada waktu kompilasi adalah yang terpenting. sejauh yang saya peduli, evaluasi runtime bisa segfault untuk kelelahan tumpukan.

Masalahnya adalah dalam situasi seperti berikut (dalam C++):

int x[2];
int y; // let's assume y is put right after the end of x in the stack frame
if (&x[0] + 2 == &y) {
  // ...
}

Kompiler C ingin (dan melakukan!) mengoptimalkan perbandingan itu ke false . Lagi pula, satu pointer menunjuk ke x dan satu lagi ke y , jadi tidak mungkin keduanya sama.
Kecuali, tentu saja, bahwa alamatnya sama di mesin karena satu pointer menunjuk tepat di akhir x , yang merupakan alamat yang sama dengan (awal) y ! Jadi, jika Anda cukup mengaburkan kode sehingga kompiler tidak melihat lagi dari mana alamat-alamat itu berasal, Anda dapat mengetahui bahwa perbandingan bernilai true . Oleh karena itu, standar C++ memungkinkan kedua hasil terjadi secara nondeterministik, membenarkan pengoptimalan (yang mengatakan false ) dan kompilasi ke perakitan (yang mengatakan true ). Standar C tidak mengizinkan hal ini, membuat LLVM (dan GCC) kompiler yang tidak sesuai karena keduanya akan melakukan pengoptimalan semacam ini.

Saya menulis ringkasan ide-ide saya tentang keamanan konstan, kesehatan konstan, dll. yang telah muncul di sini di utas ini dan/atau dalam diskusi terkait di IRC: https://www.ralfj.de/blog/2018/07/19/ const.html

Masalah ini di sini menjadi agak sulit untuk diurai karena begitu banyak hal yang telah dibahas. @oli-obk membantu membuat repo untuk masalah const-eval, jadi tempat yang baik untuk mendiskusikan sub-masalah tertentu mungkin adalah pelacak masalah https://github.com/rust-rfcs/const-eval.

@Centril menyarankan untuk menstabilkan versi minimal yang kompatibel dengan ekstensi apa pun di masa mendatang:

  • tidak ada argumen umum dengan batas sifat
  • tidak ada argumen atau tipe pengembalian pointer fn atau tipe dyn Trait

    • diperiksa secara rekursif pada tipe argumen sehingga bidang argumen mungkin juga bukan tipe ini

  • tidak ada kode yang tidak aman (karena kami tidak tahu apakah ada hal-hal di sana yang bermasalah)

    • Saya pribadi berpikir itu baik-baik saja kecuali untuk union s yang sudah berada di belakang gerbang fitur tambahan, dan deref pointer mentah (umumnya dilarang dalam konstanta apa pun sekarang). Kode tidak aman lainnya harus melalui const fns atau const intrinsik lain yang tidak aman, yang memerlukan stabilisasi diskusi mereka sendiri.

(nit: saran saya juga termasuk pemeriksaan rekursif untuk fn pointer atau dyn Trait pada tipe pengembalian const fn s)

tidak ada argumen umum dengan batas sifat

Untuk memperjelas, apakah hal seperti ini akan diterima atau tidak?

struct Mutex<T> where T: Send { /* .. */ }

impl<T> Mutex<T> where T: Send {
    pub const fn new(val: T) -> Self { /* .. */ }
}

Ikatan bukan bagian dari const fn itu sendiri.

Juga untuk memperjelas: apakah proposal untuk menstabilkan hal-hal itu dan meninggalkan sisanya di belakang gerbang fitur ATAU untuk menstabilkannya dan membuat sisanya menjadi kesalahan sama sekali?

@mark-im sisanya akan tetap berada di belakang gerbang fitur.

@oli-obk apa masalahnya dengan kode yang tidak aman? Kami mengizinkan unsafe di const X : Ty = ... , yang memiliki semua masalah yang sama. Saya pikir tubuh const fn harus diperiksa persis seperti tubuh const .

Saya pikir kami ingin tetap konservatif di sekitar operasi "tidak konstan" -- operasi yang aman tetapi tidak aman (pada dasarnya apa pun pada pointer mentah) -- tetapi itu sudah dilarang sepenuhnya dalam konteks const, bukan?

Batas bukan bagian dari const fn itu sendiri.

Tidak, batasan pada blok impl juga tidak akan diizinkan berdasarkan proposal itu

apa masalah dengan kode yang tidak aman?

Saya tidak melihat ada masalah seperti yang tercantum dalam komentar saya. Setiap fitur/fungsi const tidak aman harus melalui stabilisasinya sendiri.

@RalfJung Saya pikir masalahnya adalah " @Centril gugup karena kami melewatkan sesuatu. Itu sudah dilarang sepenuhnya dalam konteks const". ;) Tapi kita harus menstabilkan unsafe { .. } di const fn di beberapa titik jadi jika Anda yakin tidak ada masalah dan bahwa kita menangkap semua operasi unconst maka mari kita lakukan?

Selain itu, jika kami melewatkan sesuatu, kami sudah agak kacau karena orang dapat menggunakannya di const .

Saya masih berencana untuk menulis PR mengisi bagian keselamatan / promosi const dari repo RFC const ; itu akan menjadi waktu untuk memeriksa dengan cermat apakah kita membahas semuanya (dan memiliki testcases).

Hal lain yang muncul di Discord adalah operasi FP: Saat ini kami tidak dapat menjamin bahwa mereka akan cocok dengan perangkat keras nyata. CTFE akan mengikuti IEEE dengan tepat, tetapi LLVM/perangkat keras mungkin tidak.

Ini juga berlaku untuk item const , tetapi item tersebut tidak akan pernah dijalankan saat runtime -- sedangkan const fn mungkin. Jadi tampaknya bijaksana untuk tidak menstabilkan operasi FP di const fn .

OTOH, sudah kami promosikan hasil operasi FP? Jadi di sana kita sudah memiliki ketidakcocokan run-time/compile-time yang dapat diamati pada stable. Apakah layak untuk melakukan kawah untuk melihat apakah kita dapat membatalkannya?

Untuk referensi di masa mendatang, artikel berikut ini relevan sehubungan dengan floating point dan determinisme:

@RalfJung

Apakah layak untuk melakukan kawah untuk melihat apakah kita dapat membatalkannya?

Saya akan terkejut jika kita bisa melakukan ini, tetapi setidaknya patut dicoba. :)

@RalfJung mungkin ada masukan menarik lebih lanjut di utas ini https://github.com/rust-lang/rust/issues/24111#issuecomment -386764565

Dengan asumsi kami ingin mempertahankan bentuk kata kunci const fn , saya pikir menstabilkan sesuatu sekarang , itu cukup terbatas, adalah solusi yang cukup baik (bagaimana saya tidak melihatnya sebelumnya?!)

Ketika kami melakukan stabilisasi sedikit demi sedikit ini, saya hanya ingin mendaftar
meminta daftar yang jelas tentang pembatasan dan pembenarannya. Dia
akan membuat frustrasi ketika pengguna membuat perubahan yang tampaknya tidak berbahaya dan
mengalami kesalahan kompilasi, jadi setidaknya kita bisa memperbaiki kesalahan itu. saya
mengenali alasannya tersebar di banyak diskusi, tidak semuanya
Saya telah mengikuti, tetapi saya pikir seharusnya ada tabel di dokumen (atau
referensi, atau nomicon, setidaknya) daftar setiap operasi yang tidak diizinkan, the
masalah yang dapat ditimbulkannya, dan prospek stabilisasi (misalnya "tidak pernah",
"jika RFC XYZ diimplementasikan", "setelah kami menyelesaikan bagian spesifikasi ini").

Pada Senin, 20 Agustus 2018 pukul 13:44 Eduard-Mihai Burtescu <
[email protected]> menulis:

Dengan asumsi kami ingin mempertahankan bentuk kata kunci const fn, saya pikir menstabilkan
sesuatu sekarang , itu cukup terbatas, adalah solusi yang cukup baik (bagaimana
apakah saya tidak melihatnya sebelumnya ?!)


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/rust-lang/rust/issues/24111#issuecomment-414403036 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAC3n80yVxIa3agsJP4wXZFkgyHVmAsjks5uSvWXgaJpZM4D66IA
.

@est31 Seperti yang sudah ditulis oleh @rkruppe, sekering itu ilegal untuk dijalankan tanpa -ffast-math -- dan saya pikir LLVM menanganinya dengan benar.

Dari apa yang saya ingat, aritmatika dasar bahkan tidak terlalu bermasalah (kecuali pada 32bit x86 karena x87 ...), tetapi fungsi transendental. Dan itu bukan const fn , kan? Jadi harapan saya adalah bahwa pada akhirnya kita dapat memiliki keseimbangan antara item const , promosi dan const fn dalam hal ini juga.

@RalfJung Saya masih tidak yakin bahwa misalnya menumpahkan dan kemudian memuatnya kembali ke register FPU di antara beberapa operasi memberikan hasil yang sama dengan perhitungan yang sama tanpa menumpahkan itu.

Dari apa yang saya ingat, aritmatika dasar bahkan tidak terlalu bermasalah (kecuali pada 32bit x86 karena x87 ...), tetapi fungsi transendental.

Bagaimana fungsi transendental berarti masalah?

IIRC, sementara beberapa fungsi transendental didukung oleh prosesor x86 umum, fungsi tersebut lambat dan dihindari, dan hanya disertakan untuk kelengkapan dan kompatibilitas dengan implementasi yang ada. Jadi, hampir di mana-mana, fungsi transendental dinyatakan dalam kombinasi fungsi aritmatika dasar. Ini berarti bahwa tidak ada perbedaan dalam transparansi referensialnya dengan fungsi aritmatika. Jika fungsi dasar "aman" maka apa pun yang dibangun di atasnya, termasuk fungsi transendental. Satu-satunya sumber "ketidaktransparan referensial" di sini mungkin adalah perkiraan (implementasi) yang berbeda dari fungsi transendental tersebut dalam kaitannya dengan fungsi aritmatika dasar tersebut. Apakah itu sumber masalahnya?

@est31 Sementara sebagian besar fungsi transendental pada akhirnya hanya kode perpustakaan yang terdiri dari operasi integer dan float primitif, implementasi ini tidak distandarisasi dan dalam praktiknya program Rust dapat berinteraksi dengan mungkin tiga implementasi berbeda sepanjang masa pakainya, beberapa di antaranya juga bervariasi menurut host atau target platform:

  • Ada implementasi runtime yang digunakan program pada target (misalnya platform libm target, atau instruksi perangkat keras dalam beberapa keadaan)
  • Ada folder konstan yang digunakan LLVM (tampaknya ini hanya platform Host C libm)
  • Ada apa pun yang akan dilakukan MIRI dengan operasi ini (misalnya, sesuatu di rustc_apfloat , atau menafsirkan implementasi Rust seperti https://github.com/japaric/libm/)

Jika salah satu dari ini tidak setuju satu sama lain, Anda bisa mendapatkan hasil yang berbeda tergantung pada saat ekspresi dievaluasi.

@rfcbot batal

Kami tidak mungkin untuk menstabilkan monty penuh dalam waktu dekat.
Sebagai gantinya, saya ingin mengembangkan konsensus untuk subset yang lebih minimal (seperti yang diuraikan secara kasar di https://github.com/rust-lang/rust/issues/24111#issuecomment-414310119) yang semoga dapat kami stabilkan dalam waktu dekat .
Subset ini dilacak di #53555. Deskripsi lebih lanjut tersedia di sana.

Usulan @Centril dibatalkan.

@rkruppe apakah ada alasan untuk menurunkan fungsi transendental ke llvm intrinsik? Tidak bisakah kita menghindari seluruh masalah dengan menurunkannya ke implementasi anti karat yang terkenal yang kita kendalikan dan yang sama di semua platform?

apakah ada alasan untuk menurunkan fungsi transendental ke llvm intrinsik?

Selain kesederhanaan tidak mengimplementasikan libm lintas platform secara keseluruhan, intrinsik memiliki keunggulan dalam pengoptimal dan codegen LLVM yang tidak akan didapatkan oleh fungsi perpustakaan biasa. Jelas lipat konstan (& hal-hal terkait seperti analisis rentang nilai) adalah masalah dalam konteks ini tetapi sebaliknya cukup berguna. Ada juga identitas aljabar (diterapkan oleh pass SimplifyLibCalls). Akhirnya, beberapa fungsi (kebanyakan sqrt dan timbal baliknya, yang tidak transendental tetapi apa pun) memiliki dukungan pembuatan kode khusus untuk misalnya menghasilkan sqrtss pada x86 dengan SSE.

Tidak bisakah kita menghindari seluruh masalah dengan menurunkannya ke implementasi anti karat yang terkenal yang kita kendalikan dan yang sama di semua platform?

Bahkan mengabaikan semua hal di atas, ini bukan pilihan IMO yang bagus. Jika platform target memiliki C libm, seharusnya dimungkinkan untuk menggunakannya, baik karena lebih dioptimalkan atau untuk menghindari kembung.

intrinsik memiliki keunggulan dalam pengoptimal dan codegen LLVM yang tidak akan didapatkan oleh fungsi perpustakaan biasa.

Saya pikir pengoptimalan seperti itu hanya diaktifkan jika matematika cepat diaktifkan?

Jika platform target memiliki C libm, seharusnya dimungkinkan untuk menggunakannya, baik karena lebih dioptimalkan atau untuk menghindari kembung.

Tentu. Harus selalu ada opsi untuk memperdagangkan properti yang agak akademis ini -- transparansi referensial -- demi hal-hal yang lebih penting seperti peningkatan kecepatan biner yang dikompilasi atau biner yang lebih kecil. Misalnya dengan menggunakan platform libm atau dengan mengaktifkan mode matematika cepat. Atau apakah Anda menyarankan agar kita melarang mode matematika cepat selamanya?

IMO kita tidak boleh melarang f32::sin() dalam konteks const, setidaknya tidak jika kita mengizinkan + , - dll. Larangan seperti itu akan memaksa orang untuk membuat dan menggunakan peti yang menyediakan implementasi yang kompatibel dengan const.

Saya pikir pengoptimalan seperti itu hanya diaktifkan jika matematika cepat diaktifkan?

Evaluasi konstan dari fungsi-fungsi ini dan emisi sqrtss mudah dibenarkan tanpa -ffast-math, karena dapat dibuat dengan benar dibulatkan.

Atau apakah Anda menyarankan agar kita melarang mode matematika cepat selamanya?

Saya tidak menyarankan apa pun, saya juga tidak memiliki pendapat (atm) tentang apakah properti semacam itu harus dijamin. Saya hanya melaporkan kendala.

Tidak dapat menemukan masalah terbuka untuk ICE yang disebabkan oleh Vec dalam konteks const fn.

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=508238a9f06fd85720307bf6cc227586

Haruskah saya membuka masalah baru untuk ini?

@Voultapher ya itu terlihat seperti ICE baru.

Baiklah dibuka #55063.

Jika kompiler dapat memeriksa apakah suatu fungsi dapat dipanggil dalam constexpr waktu kompilasi ketika pengguna membubuhi keterangan dengan const fn , mengapa tidak secara otomatis melakukan pemeriksaan pada semua fungsi? (mirip dengan ciri-ciri otomatis)? Saya tidak bisa memikirkan dampak buruk yang nyata, dan manfaat yang jelas adalah bahwa kita tidak harus bergantung pada penilaian manusia yang rawan kesalahan.

Kelemahan utama adalah bahwa itu menjadi detail publik, jadi implementasi
perubahan dalam fungsi yang tidak dimaksudkan sebagai const sekarang rusak.

Juga kita tidak perlu bergantung pada penilaian manusia. Kami dapat memiliki clippy lint yang memberi tahu kami kapan fungsi tanpa catatan bisa menjadi const fn : https://github.com/rust-lang/rust-clippy/issues/2440

Ini mirip dengan bagaimana kita tidak menyimpulkan mutabilitas variabel lokal, tetapi sebagai gantinya kompilator memberi tahu kita di mana menambahkan atau menghapus mut .

@remexre const fn bertindak sebagai spesifikasi antarmuka. Saya tidak terlalu mengenal detail kecil dari fitur ini (dan mungkin apa yang berikut di sini sudah dipikirkan), tetapi dua kasus di mana saya dapat memikirkan kompiler yang memberi tahu ketika suatu fungsi dianotasi secara tidak benar sebagai const adalah gagal kompilasi jika fungsi tersebut menggunakan &mut sebagai parameter atau jika memanggil fungsi non- const lainnya. Jadi jika Anda mengubah implementasi const fn dan Anda melanggar batasan tersebut, kompiler akan menghentikan Anda. Kemudian Anda dapat memilih untuk mengimplementasikan bit non- const dalam (a) fungsi yang terpisah atau merusak API jika itu adalah perubahan yang diinginkan.

Ada titik tengah lain yang belum pernah saya diskusikan dan kemungkinan untuk memperkenalkan kebalikan dari penanda ini dan semacam "inferensi kemurnian fungsi" ketika tidak diatur secara eksplisit. Kemudian dokumen akan menunjukkan penanda yang sebenarnya tetapi dengan semacam peringatan tentang tidak menjamin stabilitas penanda itu jika itu adalah const . Masalahnya adalah ini mungkin mendorong menjadi malas dan melakukan ini hampir setiap waktu, yang bukan tujuannya.

haruskah const fn dapat menghasilkan output? mengapa &mut dilarang?

@aledomu Komentar saya diarahkan ke @AGaussman; Saya sedang berbicara tentang kasus di mana penulis perpustakaan mengekspos fungsi yang tidak "dimaksudkan" const (dalam hal const-ness tidak dimaksudkan untuk menjadi bagian dari API); jika const disimpulkan, itu akan menjadi perubahan besar untuk membuat fungsi tersebut non-const.

@SoniEx2 const fn adalah fungsi yang dapat dievaluasi pada waktu kompilasi, yang kebetulan hanya berlaku untuk fungsi murni apa pun.

@remexre Jika itu tidak dimaksudkan untuk menjadi bagian stabil dari API, jangan tandai itu.

Untuk sedikit inferensi yang saya komentari, itu sebabnya saya menyebutkan perlunya beberapa peringatan pada dokumen peti.

apa bedanya? sama sekali tidak ada!

const fn add_1(x: &mut i32) { x += 1; }
let mut x = 0;
add_1(&mut x);
assert_eq!(x, 1);
x = 0;
add_1(&mut x);
assert_eq!(x, 1);

const fn added_1(x: i32) -> i32 { x + 1 }
let mut x = 0;
x = added_1(x);
assert_eq!(x, 1);
x = 0;
x = added_1(x);
assert_eq!(x, 1);

Saya telah mengajukan masalah yang ditargetkan untuk:

Masalah yang ditargetkan berikut sudah ada:

Jika ada daerah lain, yang belum terlacak oleh masalah lain, itu perlu didiskusikan dengan wrt. const eval dan const fn , saya menyarankan agar orang membuat masalah baru (dan cc saya + @oli-obk di dalamnya).

Ini menyimpulkan kegunaan masalah ini, yang dengan ini ditutup.

Saya tidak memiliki semua spesifik dalam pikiran, tetapi tidak ada lebih banyak yang didukung oleh miri tetapi belum diaktifkan di min_const_fn ? Misalnya pointer mentah.

@SimonSapin Ya, tangkapan bagus. Ada beberapa masalah yang lebih yang ada untuk itu. Saya telah memperbarui komentar + deskripsi masalah. Jika ada sesuatu yang tidak tercakup yang Anda alami, tolong buat masalah baru.

Saya pikir tidak tepat untuk menutup masalah pelacakan meta ketika sama sekali tidak jelas bahwa apa yang dicakupnya dicakup secara mendalam oleh masalah yang lebih spesifik.

Ketika saya menghapus #![feature(const_fn)] di Servo, pesan kesalahannya adalah:

  • trait bounds other than `Sized` on const fn parameters are unstable
  • function pointers in const fn are unstable

( const fn s ini semua adalah konstruktor sepele untuk tipe dengan bidang pribadi. Pesan sebelumnya ada di konstruktor struct Guard<T: Clone + Copy> , meskipun Clone tidak digunakan dalam konstruktor. yang terakhir adalah untuk menginisialisasi Option<fn()> (disederhanakan) menjadi None atau Some(name_of_a_function_item) .)

Namun, baik sifat maupun tipe penunjuk fungsi tidak disebutkan dalam deskripsi masalah ini.

Saya tidak bermaksud kita harus memiliki dua masalah yang lebih spesifik untuk hal di atas. Maksud saya, kita harus membuka kembali yang ini sampai kita entah bagaimana memastikan bahwa segala sesuatu di balik gerbang fitur const_fn (yang masih menunjuk di sini dalam pesan kesalahan) memiliki masalah pelacakan. Atau sampai const_fn sepenuhnya stabil.

@SimonSapin

Saya pikir tidak tepat untuk menutup masalah pelacakan meta ketika sama sekali tidak jelas bahwa apa yang dicakupnya dicakup secara mendalam oleh masalah yang lebih spesifik.

Masalah ini memiliki rasa https://github.com/rust-lang/rust/issues/34511 yang merupakan salah satu kekacauan terbesar sejauh masalah pelacakan pergi. Masalah ini juga gratis untuk semua selama beberapa waktu sehingga tidak bertindak sebagai masalah meta sekarang. Untuk semua yang gratis, silakan gunakan http://internals.rust-lang.org/ sebagai gantinya.

Namun, baik sifat maupun tipe penunjuk fungsi tidak disebutkan dalam deskripsi masalah ini.

Saya tidak bermaksud kita harus memiliki dua masalah yang lebih spesifik untuk hal di atas.

Itulah yang menurut saya harus dilakukan. Dari perspektif triase T-Lang, adalah menguntungkan untuk memiliki masalah yang ditargetkan dan dapat ditindaklanjuti .

Maksud saya, kita harus membuka kembali yang ini sampai kita entah bagaimana memastikan bahwa segala sesuatu di balik gerbang fitur const_fn (yang masih menunjuk di sini dalam pesan kesalahan) memiliki masalah pelacakan. Atau sampai const_fn sepenuhnya stabil.

Bahkan tidak jelas bagi saya apa const_fn gerbang fitur bahkan merupakan atau bahwa semuanya akan distabilkan di beberapa titik. Segala sesuatu selain batas dan penunjuk fungsi dari RFC asli memiliki masalah terbuka dan kemudian beberapa.

Bahkan tidak jelas bagi saya apa const_fn yang merupakan gerbang fitur

Itulah mengapa kita tidak boleh menutupnya sampai kita mengetahuinya, IMO.

Semuanya

Namun, apakah itu benar-benar segalanya?

Adakah yang tahu apa yang terjadi dengan fitur const_string_new ? Apakah ada masalah pelacakan untuk itu? Buku tidak stabil hanya link di sini.

@phansch Itu karena semua rustc_const_unstable menunjuk ke sini. (cc @oli-obk bisakah kita memperbaikinya?)

Masalahnya harus terbuka saat itu. Itu hanya menghina sebagai pengguna untuk ditunjuk
untuk masalah tertutup.

Pada Rabu, 9 Jan 2019, 04:05 Mazdak Farrokhzad < [email protected]
menulis:

@phansch https://github.com/phansch Itu karena semua
titik rustc_const_unstable di sini.


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/rust-lang/rust/issues/24111#issuecomment-452622097 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAC3n7JhzsZZpizmWlp0Nww5bcfIqH2Vks5vBbC8gaJpZM4D66IA
.

@durka : Akan selalu ada kemungkinan jendela di mana sesuatu ditutup di malam hari dan resolusinya masih belum stabil. Bagaimana itu menghina?

Saya telah menolak berkomentar di sini, dan mungkin kita harus memindahkan percakapan ini ke utas di internal (sudah ada?) tapi...

Keputusan untuk menutup ini tidak masuk akal bagi saya. Ini masalah pelacakan, karena muncul dalam pesan kesalahan dari kompiler, dan itu tidak sendirian, lihat posting ini untuk beberapa contoh lagi: https://internals.rust-lang.org/t/psa-tracking-for-gated -fitur-bahasa/2887. Menutup masalah ini kepada saya menyiratkan stabilitas, yang jelas belum terjadi.

Terus terang saya tidak dapat melihat argumen untuk menutup ini ... Saya senang masalah yang lebih bertarget sekarang ada, sehingga implementasi dapat bergerak maju, dengan diskusi dan fokus yang mudah-mudahan baru, tetapi saya tidak melihat cara yang jelas untuk mengaitkan kompiler pesan dengan itu.

Sekali lagi, jika ini membutuhkan (atau sudah memiliki) utas di internal, mungkin mari kita pindahkan percakapan ini ke sana?

EDIT: Atau apakah masalahnya hanya karena buku itu sudah usang? Mencoba contoh dari RFC (tidak ada beberapa #[derive(...)] s) tampaknya berfungsi tanpa kesalahan pada Rust rustc 1.31.1. Apakah masih ada pesan kesalahan kompiler yang menunjuk ke sini? Akan menyenangkan untuk memiliki tempat untuk menautkan kesalahan seperti:

error: only int, `bool` and `char` operations are stable in const fn

Jika kami ingin mereka dikaitkan dengan masalah spesifik, itu mungkin merupakan peningkatan.

Oke, jadi di sini harus ada beberapa bukti kuat untuk masalah ini tetap terbuka. Sejauh yang saya tahu ini:

https://github.com/rust-lang/rust/blob/6ecad338381cc3b8d56e2df22e5971a598eddd6c/src/libsyntax/feature_gate.rs#L194

adalah satu-satunya fitur active yang menunjukkan masalah tertutup.

Di dunia yang ideal, saya percaya diskusi semacam ini harus benar-benar otomatis, karena seperti yang telah kami temukan, orang memiliki berbagai pendapat dan ide tentang bagaimana segala sesuatunya harus bekerja. Tapi itu benar-benar bukan percakapan untuk utas ini ...

Jika kami ingin mereka dikaitkan dengan masalah spesifik, itu mungkin merupakan peningkatan.

Ya, ini adalah solusi yang tepat, dan apa yang sudah disarankan oleh @Centril .

Komentar awal juga telah diedit untuk mengarahkan orang ke masalah spesifik yang tiba di sini di "jendela" yang disebutkan oleh @ErichDonGubler .

https://github.com/rust-lang/rust/issues/57563 kini telah dibuka untuk melacak fitur const tidak stabil yang tersisa.

Seseorang dapat mengedit badan masalah di sini untuk secara jelas menautkan ke #57563?

@glaebhoerl selesai :)

Hai, saya sampai di sini karena saya mendapat error[E0658]: const fn is unstable (see issue #24111) saat mengkompilasi ncurses-rs. Apa yang harus saya lakukan? Tingkatkan karat? aku mendapat

$ cargo version
cargo 1.27.0
$ rustc --version
rustc 1.27.2

EDIT: lakukan brew uninstall rust dan ikuti petunjuk pemasangan rustup , sekarang rustc --version adalah rustc 1.33.0 (2aa4c46cf 2019-02-28) dan kesalahan itu hilang.

Ya, agar dapat menggunakan const fn di stable, Anda harus memperbarui kompiler Anda.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat