Rust: Masalah pelacakan untuk "Macro 1.1" (RFC # 1681)

Dibuat pada 22 Agu 2016  ·  268Komentar  ·  Sumber: rust-lang/rust

Masalah pelacakan untuk rust-lang / rfcs # 1681.

cc @alexrich

Stabilisasi TODO

Tes lakmus:

Fitur:

  • Nama peti, saat ini adalah proc_macro
  • Jenis peti, saat ini proc-macro
  • Atribut #[proc_macro_derive(Foo)]
  • Memuat proc-macro peti dengan -L dan #[macro_use] untuk memuatnya
  • membayangi adalah kesalahan
  • tidak ada kebersihan
  • meneruskan aliran token untuk seluruh struct dan menerima semuanya kembali
  • Atribut manifes kargo, saat ini proc-macro = true

Bug yang diketahui:

  • [] - Panicking derive mungkin memiliki rentang yang salah - # 36935
  • [] - aliran token dengan mod foo gagal - # 36691
  • [] - dokumen tidak diterbitkan untuk proc_macro - # 38749
  • [x] - atribut khusus untuk beberapa mode itu sulit - https://github.com/rust-lang/rust/issues/35900#issuecomment -252499766
  • [x] - uji kargo gagal untuk proc macro libs - # 37480
  • [x] - pesan masih penting - https://github.com/rust-lang/rust/issues/35900#issuecomment -252430957 (di-fxed oleh https://github.com/rust-lang/rust/pull/37067)
  • [x] - Tidak dapat mendokumentasikan kotak rustc-macro - https://github.com/rust-lang/rust/issues/36820 (diperbaiki di # 36847)
  • [x] - Kargo terlalu sering dibangun kembali - https://github.com/rust-lang/rust/issues/36625 (diperbaiki di https://github.com/rust-lang/rust/pull/36776)
  • [x] - Atribut yang dihasilkan oleh compiler mempersulit kehidupan pengarang yang dibuat secara kustom - https://github.com/rust-lang/rust/issues/35900#issuecomment -245978831

  • [x] - Buat peti rustc_macro

    • [x] - Memiliki link librustc_macro ke libsyntax . Tergantung pada librustc_macro di librustc_driver
    • [x] - Beri tag rustc_macro sebagai tidak stabil dengan tajuk standar kami.
    • [x] - Hanya beri tag rustc_macro dengan #![crate_type = "rlib"] , jangan buat dylib.
    • [x] - Menerapkan API rustc_macro menggunakan libsyntax TokenStream internal
    • [x] - beri tag rustc_macro dengan item TokenStream lang sehingga kompilator mengetahuinya.
  • [x] - Tambahkan atribut rustc_macro_derive

    • [x] - memvalidasi itu dari bentuk yang tepat foo(bar) , tidak ada argumen / format lain

    • [x] - pastikan itu hanya diterapkan ke fungsi

    • [x] - pastikan itu hanya diterapkan ke fungsi di modul root

    • [x] - verifikasi tanda tangan dengan item TokenStream lang yang ditambahkan di atas

    • [x] - menyandikan semua simbol fungsi dengan rustc_macro_derive ke dalam metadata bersama dengan mode turunan yang digunakannya.

  • [x] - Tambahkan jenis peti rustc-macro untuk peti lainnya

    • [x] - sambungkan untuk menghasilkan dylib

    • [x] - pastikan dylib mendapatkan metadata

    • [x] - pastikan rustc-macro crates tidak dapat ditautkan sebagai dylib

    • [x] - pastikan tidak ada item _reachable_ selain yang diberi tag dengan #[rustc_macro_derive]

    • [x] - Tambahkan cfg(rustc_macro) sebagai perintah cfg tidak stabil, setel untuk jenis peti rustc-macro

    • [x] - pastikan bahwa rustc-macro crates terhubung secara dinamis ke libsytnax

  • [x] - Isi #[macro_use] dukungan untuk rustc-macro peti

    • [x] - perpanjang pemuat perpustakaan untuk menemukan rustc-macro peti terpisah dari dylib / rlib yang dilacak hari ini saat memasukkan peti

    • [x] - Parse metadata untuk rustc-macro peti untuk mempelajari tentang pasangan mode simbol / turunan

    • [x] - dlopen dylib

    • [x] - menghasilkan kesalahan jika mode turunan apa pun akan membayangi mode lain.

  • [x] - Tambahkan bilangan bulat kargo

    • [x] - kenali rustc-macro mirip dengan plugin = true

    • [x] - berikan --crate-type=rustc-macro jika tergantung padanya

    • [x] - pilih logika host / target yang sama untuk peti rustc-macro seperti yang ada pada peti plugin (misalnya, selalu kompilasi rustc-macro crates untuk host)

  • [x] - Tes

    • [x] - uji asap memuat sifat rustc-macro, dummy #[derive]

    • [x] - konflik nama adalah kesalahan

    • [x] - kompilasi untuk arsitektur yang salah adalah kesalahan

    • [x] - tidak dapat mengeluarkan jenis peti rustc-macro dan yang lainnya (mis. rustc-macro + dylib) adalah kesalahan

    • [x] - rentang informasi yang tidak menghebohkan

    • [x] - menghapus atribut dari struct, serta bidang

    • [x] - menambahkan impl di sebelah struct

    • [x] - kompilasi silang mencari host jenis rustc-macro crate

    • [x] - jangan memuat vanilla dylibs sebagai peti jenis rustc-macro

    • [x] - tidak dapat memiliki ekspor publik di luar fungsi turunan makro dalam kotak karat-makro

    • [x] - makro turunan harus memiliki tanda tangan yang diperlukan

    • [x] - memuat dua kotak makro dalam satu kompilasi

B-RFC-implemented B-unstable T-lang final-comment-period

Komentar yang paling membantu

Oke, saya akan melihat ini hari ini dan melihat sejauh mana yang saya capai.

Semua 268 komentar

Saya telah memperbarui deskripsi masalah dengan daftar periksa tentang apa yang perlu dilakukan. Ini mungkin tidak lengkap tetapi semoga kami dapat mencapai 90% perjalanan ke sana

Oke, saya akan melihat ini hari ini dan melihat sejauh mana yang saya capai.

Dari # 35957: kita harus mengubah nama dari librustc_macro crate. Secara khusus, ini dimaksudkan untuk menjadi peti berumur panjang yang akan memiliki hal-hal penting untuk semua penulis makro di dalamnya, jadi membatasi pada rustc_macro (yang menurut saya, setidaknya) hanya tentang ide 1.1 yang tampaknya buruk. Saya sebelumnya menginginkan libmacro untuk ini, tetapi karena macro adalah kata yang dicadangkan (dan kita mungkin menginginkannya sebagai kata kunci di masa mendatang) itu tidak mungkin. @cgswords dan saya telah menggunakan libproc_macro, dan menurut saya itu bukan nama yang buruk, meskipun saya tidak 100% puas dengannya.

@nrc : Oke, pemikiran langsung saya tentang penamaan:

  • extern crate macros; - singkat dan menarik, tetapi dapat dibaca sebagai berisi makro, daripada kode dukungan untuk menulisnya
  • extern crate macro_runtime; - persis seperti yang tertulis di kaleng
  • extern crate metarust; - tulis Rust, tentang Rust, untuk beroperasi di Rust
  • extern crate bikeshed; - dengan makro prosedural, Anda dapat memiliki warna Rust apa pun yang Anda inginkan!
  • extern crate macrame; - terdengar seperti "macro make [r]"; mungkin lebih baik tersisa untuk API "bagus" di masa mendatang daripada perpustakaan mentah.

@nrc Sepertinya aspek penting dari pertanyaan ini adalah penamaan berbagai gaya makro kita secara keseluruhan - khususnya, jika kita menggunakan libproc_macro , kita membuat komitmen yang kuat untuk "makro prosedural" terminologi. Saya tidak memiliki pendapat yang kuat di sini, tetapi saya tidak yakin apakah kita telah secara terbuka menjelajahi ruang terminologi.

Untuk lebih jelasnya, apakah Anda berpikir bahwa macro_rules ini hanya akan menjadi "makro", yaitu hal default yang kami maksud dengan makro, sementara Anda harus memenuhi syarat "makro prosedural"? Sepertinya itu rencana yang cukup masuk akal bagi saya. Dan di dunia itu, saya berpendapat bahwa libproc_macro lebih baik daripada libmacros .

Pemikiran saya di sini adalah bahwa semua makro adalah "makro", dan ketika kita perlu membuat perbedaan berdasarkan penerapan (penggunaan harus persis sama untuk semua jenis) kita menggunakan "makro prosedural" vs "makro dengan contoh". Saya ingin membuang "ekstensi sintaks" dan "plugin kompilator" seluruhnya (dan suatu hari nanti akan menggunakan kembali yang terakhir untuk plugin yang sebenarnya).

Tapi, ya, saya sangat ingin berada di belakang terminologi "makro prosedural".

@nrc Masuk akal bagiku! Meskipun "makro dengan contoh" agak sulit digunakan, istilah ini juga sangat menggugah / intuitif. Salah satu kekhawatiran saya tentang "makro prosedural" adalah bahwa "prosedur" bukanlah sesuatu di Rust. Saya ingin tahu apakah ada cara untuk membuat koneksi lebih langsung. "Fungsi makro" kurang tepat, tapi mungkin memberi Anda gambaran tentang apa yang saya maksud?

Ya, itu tidak cukup sempurna, tetapi mengingat itu adalah istilah yang terkenal / digunakan di luar Rust, saya pikir itu lebih baik daripada menciptakan istilah kami sendiri. "Makro terprogram" dimungkinkan, tetapi saya lebih suka "prosedural".

Istilah Perl untuk padanan terdekat yang dimilikinya dengan "makro prosedural" adalah "filter sumber", yang (terutama dengan pergeseran dari AST ke token) merupakan deskripsi yang cukup pas.

Mungkin 'Syntax transformers' atau 'programmatic macro' akan berfungsi dengan baik sebagai nama? Namun, saya tidak memiliki masalah dengan makro prosedural.

"Prosedural" sudah menjadi apa yang orang sebut ini, dan saya pikir itu jelas dipahami apa artinya, terutama berbeda dengan "makro dengan contoh." Saya tidak akan khawatir untuk mencoba menemukan nama lain.

Saya suka istilah "makro prosedural" untuk penggunaan biasa (atau mungkin "makro khusus"). Saya sangat suka menggunakan kata _macro_ sehingga jelas bahwa mereka dapat (pada akhirnya ...) digunakan dengan cara yang sama seperti "makro biasa". Karena alasan ini, saya tidak suka "filter sumber" (saya juga mengharapkan filter untuk hanya melepaskan data, bukan mengubahnya, meskipun saya tahu istilah tersebut digunakan untuk keduanya).

Saya baik-baik saja dengan libproc_macro atau libmacros . Saya lebih suka yang terakhir hanya karena saya tidak suka memiliki _ dalam nama peti ketika itu dapat dengan mudah dihindari. =)

Satu pertanyaan: apakah kita pernah berharap memiliki "rutinitas dukungan" yang dapat digunakan dari makro non-prosedural? Saya tahu tidak ada rencana seperti itu, tetapi jika kami melakukannya, dan kami menginginkannya dalam peti yang sama, maka libmacros akan menjadi nama yang lebih baik. =)

(Saya berpikir sedikit tentang mis., Komentar @dherman dari RFC yang sangat bersemangat.)

@nikomatsakis : Pertanyaan terkait tetapi agak berbeda adalah kasus penggunaan yang saya https://github.com/rust-lang/rfcs/pull/1561#discussion_r60459479 - akankah kita ingin fungsi implementasi makro prosedural dapat dipanggil fungsi implementasi makro prosedural lainnya?

Saya dapat dengan mudah melihat keinginan untuk mengizinkan satu turunan kustom memanggil yang lain, dan itu pada dasarnya akan membuat definisi makro prosedural itu sendiri mampu digunakan sebagai "rutinitas dukungan" seperti itu

Tapi ya, menurut saya contoh gensym @dherman cukup menarik. Tentu saja, jika jawaban atas pertanyaan saya di atas adalah "ya", gensym adalah makro (yang dapat digunakan oleh makro-demi-contoh) dan fungsi pendukung (yang dapat digunakan oleh makro prosedural).

Saya memiliki PR kargo https://github.com/rust-lang/cargo/pull/3064 yang seharusnya mencentang semua kotak "integrasi kargo" di daftar periksa.

Saya meninggalkan komentar tentang cargo PR, tapi saya rasa kami menginginkan jenis _dependency_ yang berbeda, bukan hanya jenis _package_ yang berbeda. Pertama, saya pikir ini hanya lebih baik secara estetika dan ergnomicly, tapi itu hanya pendapat saya. Tapi saya punya dua alasan konkret juga.

  • Di masa depan dengan deps publik dan swasta, penting untuk diketahui bahwa perlu diketahui bahwa deps prosedural publik dan deps publik biasa tidak perlu bersamaan. Misalnya
  • Di masa mendatang dengan makro prosedural inline / quasi-quoting, pustaka apa pun dapat digunakan pada waktu kompilasi.
  • > akankah kita ingin fungsi implementasi makro prosedural dapat memanggil fungsi implementasi makro prosedural lainnya?

Karena ini menunjukkan peti yang sama bisa menjadi dep reguler dari peti lain makro prosedural, atau dep makro dari peti lain.

Apakah serde bekerja?

Ya https://github.com/serde-rs/serde/releases/tag/v0.8.6

(kecuali untuk atribut penampung dalam beberapa kasus # 36211)

Luar biasa, terima kasih atas pembaruannya @dtolnay!

Apakah ada dokumen tentang makro ini? Saya kira, satu-satunya contoh penggunaannya adalah dalam serde, benar kan?

OK barang kargo telah mendarat. Tidak apa-apa, tetapi alangkah baiknya mengunjungi kembali https://github.com/rust-lang/rust/issues/35900#issuecomment -243976887 beberapa saat sebelum stabilisasi. [Untuk apa nilainya, saya bermaksud untuk membahas ini di RFC asli tapi lupa.]

Saya pikir "makro dengan contoh" dan "makro prosedural" bisa lebih baik dikategorikan sebagai "makro deklaratif" dan "makro imperatif". Ini memberikan kesejajaran informatif dengan kategorisasi deklaratif / imperatif yang lebih dikenal luas dari bahasa pemrograman. Karena imperatif diperlakukan sebagai superset atau sinonim dari prosedural, itu harus cukup dekat bagi orang yang terbiasa dengan terminologi "makro prosedural" untuk melakukan lompatan. Ini juga harus menghindari kebingungan dengan konsep prosedur / fungsi / metode dalam karat itu sendiri.
Skema penamaan ini memberi kita macro_imp peti dan modul paralel macro_rules . macro_rules akhirnya bisa menjadi modul yang lebih umum macro_dec peti.

@nrc , Ketika Anda merujuk ke "plugin aktual", apakah Anda menyertakan hal-hal seperti metacollect dan clippy , hal-hal seperti rustw , rustfmt , dan Rust Language Server , atau kategori program lainnya?

Untuk apa nilainya, saya agak tidak menyukai nama "dengan contoh" (karena $foo pola bukanlah "contoh" dalam pikiran saya). Deklaratif vs imperatif terdengar lebih baik bagi saya.

Bermain-main dengan ini, saya telah memperhatikan satu masalah. Sepertinya yang disediakan Rust terkadang menambahkan atribut yang diketahui oleh kompilator untuk diabaikan. Konteks ini hilang ketika melewati turunan adat. Saya berharap fungsi identifikasi yang diberikan sebagai turunan khusus menjadi no-op, tetapi itu akan menyebabkan kesalahan di sekitar atribut #[structural_match] ditambahkan.


Skrip reproduksi

(Di peti bernama demo_plugin )

#![feature(rustc_macro, rustc_macro_lib)]

extern crate rustc_macro;

use rustc_macro::TokenStream;

#[rustc_macro_derive(Foo)]
pub fn derive_foo(input: TokenStream) -> TokenStream {
    input
}

(di peti lain)

#![feature(rustc_macro)]

#[macro_use] extern crate demo_plugin;

#[derive(PartialEq, Eq, Foo)]
struct Bar {
    a: i32,
    b: i32,
}

Menghapus #[derive(Eq)] menyebabkan semuanya bekerja dengan baik.

@sgrif ah ya memang terima kasih sudah mengingatkan saya!

Jadi ada beberapa hal yang terjadi di sini:

  • Dengan #[derive(PartialEq, Eq)] , kompilator diam-diam menambahkan #[structural_match] karena secara statis memahami bahwa derivasi PartialEq memang memenuhi kontrak itu.
  • Saya yakin atribut serupa muncul dengan #[derive(Copy, Clone)] dengan #[rustc_copy_clone_marker] ditambahkan.
  • Saat ini ini berfungsi karena rentang atribut ini mengatakan "memungkinkan ketidakstabilan internal". Namun, kami kehilangan informasi span saat mengurai dan memarsing ulang.

Jadi, beberapa solusi yang bisa kami lakukan:

  • Secara konvensional, anggaplah turunan kustom lebih dulu, misalnya #[derive(Foo, Eq, PartialEq)]
  • Andalkan turunan khusus untuk menghilangkan atribut ini
  • Jangan mengeluarkan atribut khusus jika kami mendeteksi turunan khusus
  • Gunakan mekanisme yang sepenuhnya berbeda dalam kompilator untuk mengomunikasikan apa yang dikatakan atribut ini, tetapi tidak melalui AST itu sendiri.

Saya lebih memilih untuk tidak memancarkan atribut ini atau menggunakan mekanisme yang berbeda. Ini benar-benar rumit, karena #[derive(Copy, Foo, Clone)] juga perlu berfungsi (di mana kode khusus berjalan di tengah yang dapat mengubah definisi).

Tindakan pilihan saya adalah tidak mengeluarkan atribut ini jika kita mendeteksi turunan kustom. Meski begitu, mungkin tidak sepele. Untuk saat ini konvensi "custom first dan standard last" sudah cukup, tapi menurut saya kita harus memperbaikinya sebelum menstabilkan.

Penafian: Saya hanya memiliki tampilan luar dari kompiler - jadi ada banyak hal yang tidak saya ketahui. ^^

Tapi dari apa yang saya pahami, pendekatan saat ini dari makro 1.1 custom berasal lebih seperti solusi sementara. Solusi sementara mungkin diterjemahkan menjadi "cukup lama". Namun dalam jangka panjang (= macros 2.0) kami tidak akan melakukan string-parsing-round-trip lagi, yang saat ini menyebabkan hilangnya informasi span. Saya ingin tahu apakah atribut tersembunyi di AST ini adalah hal yang buruk di dunia makro 2.0. Mungkin seseorang yang memiliki lebih banyak pengetahuan tentang kompilator dapat mengetahui. Jika atribut tersembunyi ini benar-benar masuk akal di dunia masa depan itu, saya akan berargumen untuk mencari solusi dengan menempatkan kebiasaan di depan secara konvensional. Semuanya sudah merupakan solusi, bukan?

@ colin-kiegel Saya yakin Anda benar dalam arti bahwa apa yang kita lakukan saat ini _bukan_ bukti masa depan. Katakanlah Anda punya, misalnya:

#[derive(Eq, Foo, PartialEq)]

Hari ini kita akan menambahkan implementasi Eq , lalu menjalankan kode kustom untuk Foo , dan kemudian menambahkan implementasi PartialEq . Struktur dapat _mengubah_ antara Eq dan PartialEq , jadi #[structural_match] ditambahkan oleh Eq mungkin tidak benar setelah Foo berjalan.

Dalam hal itu saya setuju bahwa mereka belum tentu tahan masa depan dalam hal apa pun!

Menurut perasaan saya, kebiasaan yang diturunkan yang bergantung pada perubahan struktural umumnya tidak akan tersusun dengan baik, terlepas dari atribut tersembunyi tersebut. Akankah turunan v2.0 kustom dapat mengubah struktur item, atau akankah terbatas pada dekorasi saja?

Ya kemampuannya akan selalu ada, saya percaya (melekat pada TokenStream -> Antarmuka TokenStream) meskipun saya curiga implementasi yang wajar dari #[derive] akan mempertahankan struktur struktur aslinya.

Saya kira kita tidak bisa meminta keluaran tokenstream tidak berisi struct itu sendiri, untuk memastikan bahwa struktur tidak berubah? Input struct TokenStream akan menjadi awalan yang tidak dapat diubah? Masalah besarnya adalah memastikan untuk mengabaikan atribut yang tidak dikenali yang digunakan plugin setelah build selesai. Mungkin setiap # [derive ()] dapat memiliki awalan (katakanlah # [derive (Foo)] memiliki awalan Foo_) di mana atribut yang mereka pahami harus dimulai dengan, dan setelah memproses setiap turunan kustom, kita menghapus atribut tersebut?

@ mystor ya, masalah dengan pendekatan itu adalah atribut yang tidak dikenali, itulah sebabnya kami memiliki seluruh struct sebagai input. Itu umumnya lebih fleksibel daripada mengandalkan awalan / sufiks / registrasi / dll.

Jika turunan khusus v2.0 dapat menandai atribut khusus sebagai _used_, itu bisa dibatasi ke _read-only_ akses ke sisa aliran token item. Dengan cara ini, komposisi yang lebih baik dari turunan khusus dapat dijamin IMO. Jika makro v2.0 perlu mengubah struktur item, ia harus meng-ue api lain, tetapi tidak mendapatkan kustom. Dengan cara ini masalah dengan #[structural_mach] dan pengurutan (custom) turunan hanya akan ada di makro 1.1. Apakah itu masuk akal?

Masalah lainnya. Jika sebuah struct memiliki dua kebiasaan yang berbeda dan yang kedua panik, rentang kesalahan akan mengarah ke yang pertama, bukan yang panik.


Reproduksi Script

Di peti bernama demo_plugin

#![feature(rustc_macro, rustc_macro_lib)]

extern crate rustc_macro;

use rustc_macro::TokenStream;

#[rustc_macro_derive(Foo)]
pub fn derive_foo(input: TokenStream) -> TokenStream {
    input
}

#[rustc_macro_derive(Bar)]
pub fn derive_bar(input: TokenStream) -> TokenStream {
    panic!("lolnope");
}

Di peti lain

#![feature(rustc_macro)]

#[macro_use] extern crate demo_plugin;

#[derive(Foo, Bar)]
struct Baz {
    a: i32,
    b: i32,
}

Kesalahan akan menyorot Foo meskipun Bar panik.

Terima kasih atas laporannya @sgrif! Saya telah memperbarui deskripsi masalah ini dan saya berharap dapat melacak semua masalah yang belum terselesaikan yang terkait dengan makro 1.1 di sana juga.

Hmm, interaksi custom derive dengan #[structural_eq] adalah sesuatu yang belum pernah saya pikirkan sebelumnya. Ini agak menggangguku!

Menurut saya, memiliki cara untuk "menambahkan" teks ke aliran token mungkin merupakan antarmuka yang lebih baik di penghujung hari ... cara ini akan menjaga informasi span dan menghindari masalah ini, bukan?

Satu keuntungan dari antarmuka yang lebih umum adalah memungkinkan paket memiliki atribut pada bidang, yang dapat dihilangkan saat makro dijalankan. Ini adalah satu-satunya kasus penggunaan yang saya ketahui karena mengizinkan makro turunan khusus untuk mengubah item asli.

Saya pikir pertanyaan antarmuka turun ke apakah kita ingin turunan kustom menjadi 'hanya makro lain', dalam hal ini tampaknya penting untuk memiliki antarmuka yang sama dengan makro prosedural lainnya (di mana kita ingin memodifikasi item asli). Atau apakah itu harus menjadi miliknya sendiri dengan antarmuka khusus, dalam hal ini antarmuka (tambahkan) yang lebih ketat masuk akal.

Saya akan mencatat bahwa ekstensi sintaks lama memiliki perbedaan antara pengubah dan dekorator, dan saya pikir semua orang yang terlibat sangat membenci perbedaan itu. Oleh karena itu, saya agak enggan untuk mengambil jalur yang memiliki turunan khusus menjadi sedikit istimewa (alternatif yang telah dibahas adalah semacam turunan adat yang sangat khusus, bahkan mungkin format deklaratif).

@nrc yah, itu masih bisa menjadi tokenstream -> tokenstream, di mana kami tidak mengekspos metode new yang dimulai dari awal, bukan?

Saya setuju bahwa atribut khusus pada bidang harus dapat dibuat agar turunan khusus benar-benar masuk akal. Tapi saya yakin mungkin ada banyak cara untuk melakukannya dengan gaya "append" - ini tentu saja harus didiskusikan. Saya pasti lebih suka gaya append, karena saya lebih suka dunia di mana makro yang diturunkan tidak mengubah item dan dapat disusun, ditambah itu memecahkan masalah #[structural_eq] . Ini adalah jumlah kebebasan yang tepat IMO.

Jika orang tidak menyukai takdir itu, saya ingin bertanya mengapa, karena jelas ada cukup alasan untuk membuat perbedaan ini sebelumnya, bukan?

(Saya kira yang saya sarankan hanyalah peretasan sementara, tidak benar-benar mengatasi masalah komposabilitas jangka panjang.)

Berbagai perpustakaan saya saat ini memiliki makro yang terlihat seperti foo!(Bar, parameters...) , yang menghasilkan struct Bar dari parameter.

Selama beberapa diskusi di IRC, kami mendapat ide untuk menulis #[derive(Foo)] #[params] struct Bar; , dan mengganti foo! dengan #[derive(Foo)] yang akan menghasilkan isi dari struct.

Ini jelas bukan argumen yang kuat, tapi saya sangat menyukai ide itu, karena lebih jelas bagi pengguna bahwa struct sedang dibangun.

Saya ingin tahu apakah kita bisa mengerjakan ulang #[structural_match] untuk ditempatkan pada impl yang dihasilkan sebagai gantinya.

(Tidak benar-benar menyelesaikan masalah)

Perlu dicatat bahwa baik Serde dan Diesel banyak menggunakan atribut khusus di lapangan, jadi ada kebutuhan yang pasti akan turunan khusus untuk memungkinkan penggantian.

Jadi sebenarnya mungkin saya tidak berpikir jernih tentang masalah #[structural_match] . Lagipula, apa yang bisa dilakukan oleh custom derive?

  • Jika custom derive memasukkan atribut #[structural_match] secara salah, pemeriksaan stabilitas akan gagal. Jika tidak, sepertinya itu bug itu sendiri! (Mengingat cara kerja yang rumit dan aneh kode ini, itu tidak akan mengejutkan saya.)
  • Jika kebiasaan yang diturunkan menghapusnya, tidak ada kerugian yang dilakukan.

Maaf telah menulis banyak komentar kecil dan berpikir keras, tetapi ada satu masalah lain. Meskipun turunan khusus mungkin tidak dapat "memalsukan" anotasi #[structural_match] (karena akan berakhir tanpa "rentang ajaib"), itu mungkin akan mengacaukan rentang dari setiap anotasi yang ada, kecuali turunan diterapkan dalam urutan yang benar, yang sangat disayangkan. Pada dasarnya sebuah contoh dari non-komposabilitas yang telah dibicarakan @ colin-kiegel, tetapi tanpa upaya untuk memodifikasi struct dalam penerbangan.

(Dengan kata lain, karena kami mengandalkan span untuk menilai apakah barang stabil dapat digunakan, kehilangan informasi span dapat menyebabkan beberapa masalah rumit di sana.)

EDIT: Oke, membaca kembali Saya melihat bahwa saya baru saja mengulang apa yang sudah dilaporkan @sgrif . Maaf lagi. ;)

Ini juga agak menjijikkan, karena itu berarti kami mengekspos detail implementasi yang tidak stabil ke kode stabil. Kode yang idealnya stabil tidak akan pernah tahu bahwa penjelasan #[structural_match] ada.

@nikomatsakbaik , dengan satu atau lain cara, kita perlu menerapkan batasan yang berbeda bergantung pada apakah makro dimaksudkan sebagai

@ colin-kiegel

Saya pasti lebih suka gaya append, karena saya lebih suka dunia di mana makro yang diturunkan tidak mengubah item dan dapat disusun, ditambah lagi itu memecahkan masalah # [structural_eq]. Ini adalah jumlah kebebasan yang tepat IMO.

Saya pikir makro yang bermutasi dapat disusun, meskipun tentu saja persyaratan komposabilitasnya berbeda. Harus ada sekumpulan kondisi sebelum dan sesudah operasi turunan, baik yang diberlakukan oleh kompiler atau konvensi. Non-mutasi tampak seperti salah satu ekstrim pada spektrum invarian yang mungkin kita pilih di sini, dan perhatikan bahwa kita sudah membahas cara-cara yang dapat diperhalus (misalnya, menandai atribut yang digunakan). Saya pikir secara umum, kami lebih memilih kondisi yang paling sederhana dan menerapkannya oleh kompiler. Namun, ini entah bagaimana dimasukkan oleh pertanyaan tentang bagaimana kebiasaan khusus harus diperlakukan.

Jika orang tidak menyukai takdir itu, saya ingin bertanya mengapa, karena jelas ada cukup alasan untuk membuat perbedaan ini sebelumnya, bukan?

Saya tidak percaya ada motivasi yang kuat saat itu. Saya pikir itu mudah untuk diterapkan dan 'sepertinya ide yang bagus'. Ini tidak disukai karena membuat implementasi lebih kompleks, menambahkan perbedaan untuk penulis makro yang biasanya tidak relevan, dan membuat makro kurang fleksibel karena harus memilih apakah akan memodifikasi atau menghias.

Saya sangat ingin mempertimbangkan desain custom derive jangka panjang, dan memastikan kita menuju ke arah yang benar. Bagi saya tampaknya kendala dari solusi 1.1 dan keinginan untuk melakukan sebanyak mungkin secepat mungkin mengotori air di sini dan kami kehilangan pandangan akan visi yang lebih besar.

Saya setuju dengan @jimmycuadra karena tampaknya mendukung atribut khusus dengan satu atau lain cara merupakan persyaratan yang sulit. @nikomatsakis juga benar karena perlakuan saat ini terhadap #[derive(PartialEq, Eq)] bawah standar dan kita seharusnya tidak menstabilkannya. Terakhir, @mystor memiliki poin yang turunan khusus bahkan tidak boleh mengetahui tentang atribut magis ini. Kami pasti ingin menambahkan lebih banyak di masa mendatang dan saya tidak ingin makro 1.1 mencegah kami melakukan itu.

Juga menggemakan sentimen @nrc tentang desain jangka panjang custom derive, saya pikir sebagian besar dari ini bermuara pada bagaimana sebenarnya #[derive] bekerja. Jika dan ketika kami mendukung atribut sewenang-wenang, saya pikir @nrc memiliki poin bagus tentang hanya memiliki pengubah dan tidak memiliki dekorator, tetapi #[derive] cukup istimewa di mana turunan khusus tidak mendefinisikan atribut baru melainkan hanya menempel pada yang sudah ada.

Saat ini implementasinya memiliki ekspansi kiri-ke-kanan yang ketat dari #[derive] mode. Semua mode turunan internal diperluas dalam satu loop dan setiap kali mode turunan kustom dipukul, kami melakukan reserialize, memperluas, lalu kembali ke tahap 1. Hal ini berarti bahwa compiler dapat menjalankan atribut #[derive] _ beberapa kali untuk satu ketik definisi_. Itu mengarah ke banyak rambut.

Satu proposal yang mungkin saya miliki adalah mengubah urutan ekspansi #[derive] :

  • Pertama, semua atribut turunan khusus dari makro 1.1 diperluas satu per satu. Artinya, jika Anda memiliki #[derive(Clone, Foo)] pertama-tama kami akan mendapatkan Foo mana struct memiliki anotasi #[derive(Clone)] . Jika #[derive] bertahan, kami akan mendapatkan sifat bawaan khusus Clone .
  • Kedua, semua atribut turunan yang tidak diketahui oleh kompilator diperluas ke atribut #[derive_Bar] . Ini hanyalah peretasan kompatibilitas mundur yang harus kita hapus di beberapa titik, dan bagaimana atribut digunakan untuk diperluas.
  • Akhirnya, kompilator memperluas semua atribut #[derive] dikenal dan terpasang

Ini memiliki efek yang mengejutkan bahwa Anda tidak mengembangkan kiri-ke-kanan, tetapi sekali lagi ingat bahwa ini hanya #[derive] . Ini memberi kompiler pengetahuan maksimal tentang definisi struct dan ketika memperluas sifat bawaan kita tahu bahwa struktur tipe tidak akan pernah berubah.

Bagaimana kedengarannya? Saya percaya itu menyelesaikan semua kendala di sini?


@nikomatsakis Saya tidak yakin bahwa strategi penempatan atribut pada impl akan berfungsi karena mode turunan kustom lainnya secara teori dapat mengubah tata letak struct, bahkan jenis bidang. Ini akan melanggar asumsi kompiler ketika pertama kali diperluas, saya kira.

Apakah urutan pemrosesan turunan pernah secara resmi dinyatakan sebagai kiri-ke-kanan, melalui referensi Rust atau semacamnya? Secara lebih umum, apakah urutan atribut penting? Kedengarannya seperti kebetulan bahwa itu diterapkan seperti itu, dan penulis makro seharusnya tidak mengandalkan urutan kiri-ke-kanan. Proposal Alex untuk memproses kustom diturunkan terlebih dahulu sehingga mereka tidak pernah melihat atribut magis yang ditambahkan oleh kompilator sangat masuk akal.

Saya hanya ingin menambahkan bahwa saya tidak menyukai gagasan bahwa custom derives dapat mengubah tata letak struct. Saya ingin dapat menggunakan ini untuk sesuatu yang sensitif terhadap keselamatan. Sebagai contoh, pertimbangkan implementasi #[derive(Trace)] digunakan oleh rust-gc .

#[derive(Trace)]
struct Foo {
    a: Gc<i32>,
}

Memperluas ke:

struct Foo {
    a: Gc<i32>,
}

unsafe impl Trace { // NOTE: Strawman impl
    unsafe fn trace(&self) { Trace::trace(&self.a) }
}

Namun, jika kami mengizinkan perubahan bidang di struct, kami dapat menentukan Evil custom derive:

#[derive(Evil)]
struct Foo {
    a: Gc<i32>,
}

Memperluas ke:

struct Foo {
    a: Gc<i32>,
    b: Gc<i32>,
}

Yang mana, jika kita menggabungkannya:

#[derive(Trace, Evil)]
struct Foo {
    a: Gc<i32>,
}

Memperluas ke:

struct Foo {
    a: Gc<i32>,
    b: Gc<i32>,
}

unsafe impl Trace {
    unsafe fn trace(&self) { Trace::trace(&self.a) }
}

Yang merupakan implementasi yang tidak benar dari Trace . Ketika digunakan dengan rust-gc , ini memungkinkan b menjadi referensi yang menjuntai, yang sangat tidak aman dan tidak sehat. Ini berarti bahwa Trace bukanlah hal yang aman untuk #[derive] lagi pada suatu jenis, yang sangat disayangkan.

Saya pribadi merasa bahwa setiap #[derive] berperilaku baik tidak akan mengubah tata letak / riasan suatu struktur, dan jika demikian maka Anda hanya kurang beruntung. Kemampuan untuk turunan khusus untuk melepaskan atribut sangat penting, dan menyerah itu bukan permulaan. Selain itu, implementasi lain yang melibatkan beberapa bentuk daftar putih atau yang lainnya sangat berbeda dari antarmuka sederhana yang kita miliki saat ini.

Dengan kata lain, saya tidak merasa bahwa "kemurnian" dari memiliki #[derive] tidak pernah memodifikasi struct sebanding dengan biayanya.

Saya hanya ingin tahu apakah akan ada cara untuk melepaskan atribut tanpa mengizinkannya menambah atau menghapus bidang (misalnya, memverifikasi bahwa bidang dalam struct yang diurai ulang sama dengan struct asli, dan membuat kesalahan jika tidak ' t, tetapi tidak mengeluh jika ada hal lain yang diubah).

Saya punya firasat buruk tentang mengizinkan derive untuk mengubah struct. Contoh @mystor adalah yang ada dalam pikiran saya ketika saya berbicara tentang komposabilitas .. pada level tinggi (mungkin istilah itu bukan istilah yang tepat).

Saya pikir orang akan memanfaatkan ini, jika tersedia. Dan hal ini akan memaksa konsumen untuk mempertimbangkan detail implementasi custom derive dan urutan eksekusi mereka.

Saya lebih suka jika saya dapat mengatakan 'hei, saya tidak tahu apa fungsi turunan ini, tapi saya memahami yang lain' tanpa saling ketergantungan. Kalau tidak, itu akan menjadi sakit di jari kaki dan saya yakin itu akan terjadi.

Apakah makro prosedural melakukan sesuatu yang berbahaya benar-benar berbeda dari peti mana pun yang Anda gunakan melakukan sesuatu yang berbahaya? Setiap peti dapat memiliki kode yang tidak aman di dalamnya yang melakukan sesuatu yang tidak semestinya. Sepertinya kasus ini hanya berkaitan dengan bagaimana Anda menentukan kepercayaan dari kode apa pun yang tidak Anda tulis sendiri, misalnya reputasi komunitas, memeriksa sumbernya sendiri, dll.

Saya tidak berpikir peti akan mencoba melakukan sesuatu yang berbahaya, sebaliknya saya berharap mereka menjadi "pintar" dan melakukan trik rapi untuk membuat penerapannya lebih efisien atau karena mereka bisa, dan untuk itu merusak implementasi turunan khusus lainnya. Saya tidak akan terkejut jika beberapa custom mulai menambahkan field ke struct yang hanya digunakan dalam implementasinya karena mereka bisa, dan kemudian merusak sesuatu seperti Trace.

@mystor Kedengarannya relevan secara teori, tetapi perlu diingat bahwa Anda sebenarnya perlu menyediakan semua bidang struktur di Rust, jadi kemungkinan besar tidak akan bekerja secara diam-diam seperti itu, daripada di C ++, misalnya.

@alexrichard

Satu proposal yang mungkin saya miliki adalah mengubah urutan ekspansi #[derive]

Aku suka ide ini. Mungkin hal yang perlu disebutkan dalam kaitannya dengan dokumentasi hanyalah bahwa urutan perluasannya "tidak ditentukan". Kami kebetulan memperluas PartialEq / Eq akhir-akhir ini, tetapi tidak ada alasan tegas untuk melakukannya.

Adapun untuk mendapatkan memodifikasi definisi struct, saya setuju itu terdengar agak halus, tetapi itu tidak terlalu mengganggu saya. Saya juga berpikir bahwa bidang "penyisipan otomatis" bisa sangat berguna - tetapi saya lebih suka pengubah seperti itu menjadi atribut yang berbeda daripada dimasukkan ke dalam daftar turunan, terutama karena kami tidak ingin orang bergantung pada pesanan ekspansi sekarang.

PS. Saya akan secara serius mempertimbangkan untuk menggunakan RNG (deterministik) yang diunggulkan oleh hash peti atau sesuatu seperti itu untuk menyusun ulang perluasan turunan kustom pengguna, sehingga orang tidak dapat mengandalkan urutan perluasan. Saya selalu ingin melakukan ini dalam beberapa konteks untuk mencegah ketergantungan implisit tetapi tidak pernah mendapat kesempatan. ;)

EDIT: Saya telah berubah pikiran , dan tidak melihat alasan untuk melarang mutasi struct lagi, tapi inilah komentar asli saya untuk konteks

Jadi, dari pemahaman saya, ini adalah argumen untuk mengizinkan kustom #[derive] untuk memutasi struct:

  • Ini bisa berguna dalam beberapa kasus (belum melihat contoh apa pun, tetapi saya yakin mereka ada)
  • Kami ingin dapat menghapus atribut setelah digunakan
  • Memberi lebih banyak kekuatan untuk penulis turunan kustom

Sementara argumen untuk menambahkan batasan ke implementasi kustom #[derive] (seperti membutuhkan nama struct dan field / nama field dalam struct untuk tetap sama) adalah:

  • Hal ini memungkinkan kode yang dihasilkan oleh kustom #[derive] bergantung pada struktur tipe yang diturunkan untuk kesehatan (mis. #[derive(Trace)] dari rust-gc yang _must_ lihat jenis dukungan yang sebenarnya, atau tidak sehat, berpotensi dengan cara penggunaan-setelah-bebas-y yang halus)
  • Ini mengurangi kemungkinan ketergantungan implisit dalam ekspansi makro, karena ada lebih sedikit informasi yang disampaikan di antara mereka melalui struct.

Menurut pendapat saya, kemampuan untuk menulis implementasi turunan suara kustom yang menghasilkan unsafe trait impls atau kode yang bergantung pada kode yang tidak aman sangat penting, dan saya percaya bahwa ada cara untuk mencapai sebagian besar kemampuan di bagian pertama ( dengan pengecualian kemampuan untuk menambahkan bidang struct) dengan cara yang aman. Jika kami tidak memiliki batasan tertentu, saya tidak berpikir bahwa peti seperti rust-gc akan dapat diterapkan dengan cara yang aman. Saya punya dua gagasan:

Ide 1

Sebelum menjalankan custom derive pass, baca nama struct, dan nama masing-masing bidang. Ketika pass selesai, dan struct diurai ulang, periksa apakah nama struct sama, dan apakah nama masing-masing field (dan jumlah field) sama. jika tidak, buat kesalahan dan matikan build.

Ini akan memastikan bahwa properti struktural dasar yang kami harapkan bergantung pada plugin turunan khusus tidak rusak, dan berarti kami memiliki lebih banyak, suara, plugin turunan khusus. Ini juga memiliki keuntungan karena kompatibel ke belakang dengan pendekatan saat ini, jadi jika kami memutuskan untuk lebih menyukainya di masa mendatang, kami dapat mengganti dan memecahkan kode siapa pun. Ini juga menangani kasus atribut yang tidak digunakan, seperti hari ini.

Ide 2

Berikan setiap plugin turunan kustom TokenStream input yang sama (teks asli yang tertulis dalam program). Saat hasilnya diurai ulang, catat atribut mana yang masih ada di struct keluaran. Jika atribut ada di setiap keluaran tokenstream, maka keluhkan tentang atribut yang tidak digunakan.

Ini berarti bahwa tidak mungkin memiliki dependensi pengurutan (karena secara konseptual, setiap plugin turunan kustom berfungsi dari objek asli yang sama), dan itu juga membuatnya tidak mungkin untuk mengacaukan struktur plugin. Saya suka ide ini karena memastikan bahwa setiap turunan kustom bertindak dengan cara yang paling waras, hanya menghasilkan item baru berdasarkan struct yang ada. Ini juga mungkin mudah untuk diubah menjadi solusi apa pun yang kami dapat ubah menjadi solusi saat ini.

TL; DR

Singkatnya, saya ingin memahami apa keuntungan khusus dari mengizinkan mutasi struct, dan mengapa hal itu melebihi masalah keamanan untuk membuat #[derive(Trace)] dan selalu benar #[derive(Serialize)] dll. Mungkin. Saya yakin bahwa jika kita akhirnya turun ke rute struct yang bermutasi, akan ada alasan yang bagus, tetapi saya akan sangat sedih untuk mengubah nama kebiasaan Trace saya yang diturunkan menjadi #[derive(unsafe_Trace)] .

Saya menemukan solusi @alexcrichton sebagai pertukaran yang baik. Saya pasti akan mengharapkan bahwa setiap perubahan yang dilakukan oleh beberapa kustom, yang default diterapkan padanya.

Meskipun @mystor memiliki poin bagus yang dapat menyebabkan kejutan yang tidak menyenangkan, memiliki kemungkinan untuk mengubah struct tampaknya wajib. Di sisi lain, peti yang menggabungkan kasus penggunaan makro prosedural, masalah keamanan kode _and_ yang tidak aman tampaknya agak tidak umum.

Di luar topik: akankah penerapan ini memberikan cara bagi makro untuk melaporkan kesalahan dengan baik?

Saya suka ide @nikomatsakis untuk mengacak urutan perluasan custom turives. Itu pasti akan membantu mencegah kebiasaan yang "buruk" menjadi terlalu populer - dan seharusnya tidak terlalu banyak usaha untuk dilakukan.

@mystor opsi ketiga adalah melakukan pemeriksaan keamanan ini hanya sekali setelah semua turunan diterapkan. Itu tidak akan 100% terdengar (dua turunan dapat menambah dan menghapus bidang yang sama), tetapi dalam hal tindakan penanggulangan heuristik, itu harus cukup untuk mencegah setiap upaya untuk mengubah definisi struct dalam turunan kustom di tempat pertama.

Saya benar-benar tidak melihat kekhawatiran seputar modifikasi struct. Bidang tidak dapat ditambahkan tanpa terlihat, sesuatu harus diperhatikan selama inisialisasi. Jika Anda benar-benar khawatir tentang hal itu, Anda dapat menulis turunan Anda untuk menghasilkan kode yang gagal dikompilasi jika tidak dapat melihat keseluruhan struct dengan mudah.

@sgrif mungkin benar dalam kasus _most_ - tetapi tidak terlalu banyak jika Anda juga mendapatkan dan menggunakan sifat default, atau sesuatu yang setara.

@sgrif PS: Memang benar bahwa sebagian besar penulis rust mungkin memahami apa yang terjadi dalam kode mereka sendiri dan oleh karena itu tidak terkejut dengan perubahan struct jika mereka memilih untuk menggunakan makro seperti itu dengan sengaja.

Kasus penggunaan umum untuk menerapkan makro pada struct tentunya adalah _combination_ dari struct-alterations + decorations. Tapi saya perkirakan rasio umum adalah "banyak dekorasi" dengan "hanya sedikit perubahan". Ada baiknya memiliki pemisahan yang jelas di sini, karena hal itu meningkatkan keterbacaan dan fleksibilitas.

  • fleksibilitas: Katakanlah Anda ingin menerapkan dua turunan khusus yang keduanya melakukan keduanya, yaitu mengubah dan menghias. Tidak peduli bagaimana Anda memesannya, Anda mungkin tidak mendapatkan hasil yang Anda inginkan. Namun, jika perubahan terjadi melalui mekanisme yang berbeda dan semua dekorasi diterapkan setelah itu, Anda memiliki fleksibilitas untuk menggabungkan beberapa perubahan dan dekorasi dengan cara yang lebih dapat dikontrol.
  • keterbacaan: Jika Anda membaca kode orang lain dan ada 10 turunan yang diterapkan ke struct dan salah satunya mengubah struktur, Anda akan memerlukan lebih banyak waktu untuk mengetahuinya.

Jadi, sementara saya merasa bagian dari argumen @mystor ini menarik:

Hal ini memungkinkan kode yang dihasilkan oleh # [derive] khusus untuk bergantung pada struktur jenis yang diturunkan untuk kesehatan (mis. # [Derive (Trace)] dari rust-gc yang harus melihat jenis dukungan yang sebenarnya, atau itu tidak sehat, berpotensi dengan cara penggunaan-setelah-bebas-y yang halus)

Saya pikir mencoba untuk menegakkan ini secara turun-temurun mungkin cara yang salah untuk melakukan sesuatu. Secara khusus, kita mungkin melakukan ingin kemampuan untuk memodifikasi definisi struktur dalam kasus umum (ya, itu tidak akan transparan, tapi jadi apa). Yang berarti bahwa gagasan memiliki "suara" Trace yang dapat dijamin bahwa struct tidak berubah setelahnya mungkin perlu dipecahkan dengan cara _other_. Pertimbangkan apakah dekorator diterapkan dari bawah ke atas:

#[mangle] // <-- custom decorator that does bad things to struct definition
#[derive(Trace)]
struct Foo {
    x: T, y: U
}

Satu pemikiran mungkin bahwa Trace impl dapat ditulis sedemikian rupa sehingga _it_ gagal untuk dikompilasi jika definisi struct berubah. Sebagai contoh:

unsafe impl Trace for Foo {
    fn trace(&self) {
        let &Foo { ref x, ref y } = self;
        <T as Trace>::trace(x);
        <U as Trace>::trace(y);
    }
}

Perhatikan bahwa #[mangle] juga dapat mengacaukan impl Anda jika itu benar-benar jahat. =) Hanya ada banyak yang bisa kita lakukan di sini.

Sebagai pengamat percakapan ini, saya akan senang memiliki aturan formal atau informal bahwa #[derive] hanya diizinkan untuk menambahkan impl blok dan memperkenalkan anotasi saudara ( #[mangle(Foo, Bar)] suara bagus untuk saya 😸) yang didedikasikan untuk _mengubah_ struktur suatu tipe. #[mangle] dapat memiliki urutan evaluasi yang ditentukan.

Mungkin perlu ada urutan evaluasi yang ditentukan di antara anotasi jika belum ada.

Saya pikir pendapat saya tentang hal ini telah mengendur. @nikomatsakis membuat poin yang bagus bahwa bahkan jika kita menyingkirkan struct yang bermutasi dalam # [derive] kita tidak akan bisa lepas dengan dapat membuat asumsi tentang layout struct dalam kode. Trik let Foo{ ... } sepertinya akan bekerja untuk memastikan bahwa tata letak benar dalam kasus yang waras. Namun, kami mungkin perlu mendokumentasikannya di suatu tempat sehingga semua orang tidak harus menemukannya secara mandiri.

@nikomat

  • mh .. mungkin ada aturan tambahan, bahwa _custom dekorator harus diterapkan sebelum derive_, ditambah _derives tidak boleh mengubah item_. Ini masih akan memungkinkan untuk mengeras / memurnikan mekanisme turunan secara umum.

Tapi saya juga senang melihat ada cara lain untuk memperkuat turunan kustom _individually_ terhadap perubahan selanjutnya. :-)

Mengenai kasus yang perlu memodifikasi interior item tempat atribut diterapkan, saya baru saja menemukan utas "Umpan balik pra-implementasi untuk Qt dengan Rust" di u.r-l.o , dan membuat posting ini:

https://users.rust-lang.org/t/pre-implementation-feedback-for-qt-with-rust/7300/19

Satu aspek penting adalah bahwa di sini, saya menyarankan #[derive] (atau yang serupa) diterapkan ke _trait_, daripada sebuah struct - dan item yang ditambahkan di dalamnya akan menjadi metode ciri const .

Mirip dengan masalah kargo yang saya kemukakan di atas, saya tidak yakin

#[macro_use]
extern crate double;

mengimpor makro prosedural dari peti bernama double . Karena kita menggunakan kode runtime (seperti dalam Ruest nyata) pada waktu kompilasi, seharusnya ada pernyataan impor bertahap yang serupa dengan Racket (require (for-syntax ...)) . [Dokumen raket adalah https://docs.racket-lang.org/reference/require.html#% 28form ._% 28% 28lib._racket% 2Fprivate% 2Fbase..rkt% 29._for-meta% 29% 29, sayangnya saya tidak tahu cara menautkan bagian yang benar.]

Posting blog http://blog.ezyang.com/2016/07/what-template-haskell-gets-wrong-and-racket-gets-right/ menunjukkan kesalahan pentahapan yang dibuat di Template Haskell dan mungkin menarik- --Aku tidak ingin membuat kesalahan yang sama di Rust.

@ Ericson2314 Perbedaan di Rust adalah bahwa double sudah dikompilasi _ untuk fase_ tertentu.
Artinya, peti hanya mengekspor antarmuka makro / pengubah, seolah-olah mereka telah didefinisikan dengan, misalnya macro_rules .
Mampu membuat peti yang mengekspor makro prosedural dan item Rust yang mendasari (yang membentuk makro prosedural) akan menarik, tetapi sejauh ini tampaknya tidak diusulkan dalam kapasitas apa pun.

Mungkin masuk akal untuk membiarkan peti yang sedang dibangun memilih banyak tentang apa dan bagaimana mengekspor, tetapi hanya mengambil sistem grosir dari LISP dengan model kompilasi yang berbeda tampaknya tidak produktif.

Ya @eddyb Saya skeptis dengan metodologi "buat tahu bagaimana itu akan digunakan di hilir". Jika ada fase yang lebih penting bagi kami daripada Racket karena model kompilasi kami (saya bahkan tidak yakin apakah Racket dapat mengkompilasi silang), jadi saya tidak mendapatkan argumen terakhir Anda.

Dinominasikan untuk diskusi tim lang re: rencana stabilisasi.

Di sisi serde, berikut adalah daftar singkat masalah yang tersisa sebelum kami dapat berhenti mendukung plugin kompilator yang ada dan secara resmi merekomendasikan Macro 1.1 untuk semua pengguna nightly: https://github.com/serde-rs/serde/issues/545. Satu-satunya hal yang kita butuhkan dari Rust adalah # 36211 diperbaiki. Segala sesuatu yang lain kami sedang membuat kemajuan cepat.

Saya memiliki PR terbuka yang mengimplementasikan rustc_macro kami tanpa menggunakan syntex, jadi kami dapat berhenti mengkhawatirkan waktu kompilasi https://github.com/serde-rs/serde/pull/548.

EDIT: sudahlah, # 36211 hanya memengaruhi implementasi sinteks lama

Saya akan mencoba menyelesaikan pelabuhan Diesel pada hari Jumat sehingga saya dapat mengonfirmasi bahwa ini melakukan semua yang kami butuhkan pada akhirnya.

@dtolnay diberi serde-rs / serde # 548, apakah ada pemblokir yang tersisa untuk serde?

@sgrif luar biasa, terima kasih! Setelah Anda melakukannya (atau sebelumnya) dapatkah Anda berkomentar di sini dengan pemblokir yang tersisa yang Anda temui?

Ya saya akan.

Pada Sel, 27 Sep 2016, 19.29 Alex Crichton [email protected]
menulis:

@dtolnay https://github.com/dtolnay diberikan serde-rs / serde # 548
https://github.com/serde-rs/serde/pull/548 , apakah masih ada yang tersisa
blocker untuk serde?

@sgrif https://github.com/sgrif luar biasa, terima kasih! Setelah Anda selesai melakukannya
(atau sebelumnya) dapatkah Anda berkomentar di sini dengan pemblokir yang tersisa
ditemui?

-
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/rust-lang/rust/issues/35900#issuecomment -250028743,
atau nonaktifkan utasnya
https://github.com/notifications/unsubscribe-auth/ABdWK7MnA1rpn-WGji8gwAT2JCIqB4LFks5quaa-gaJpZM4JqEAX
.

@alexrichard

diberikan serde-rs / serde # 548, apakah ada pemblokir yang tersisa untuk serde?

Tidak, jika @ oli-obk atau @erickt mengulas bahwa PR hari ini atau besok maka saya dapat mendorong semuanya keesokan harinya dan memigrasi beberapa pengguna terkemuka seperti Rusoto.

@dtolnay Saya banyak menggunakan serde_macros di Ruma dan ingin membantu Anda menguji serde_derive juga, jika Anda membutuhkan lebih banyak perhatian.

Sebenarnya, saya juga menggunakan diesel_codegen, jadi Ruma adalah tempat pengujian yang baik untuk Macros versi 1.1 dari kedua pustaka ini.

Saya merilis Serde 0.8.10 mengumumkan bahwa serde_macros tidak digunakan lagi dan mendukung Macros 1.1.

Mari tambahkan kotak centang untuk "cargo doc works" - https://github.com/rust-lang/cargo/issues/3132.

@dtolnay selesai!

Tidak yakin apakah ini bug serde_derive atau bug rustc / rustdoc, tetapi saya memperhatikan dua masalah dalam dokumen yang dibuat:

  • Literal "///" muncul di awal jenis docstring yang dihasilkan yang menggunakan turunan khusus.
  • Deserialize dan Serialize tidak muncul di bagian "Implementasi Sifat" untuk tipe yang menggunakan turunan kustom.

Literal "///" muncul di awal jenis docstring yang dihasilkan yang menggunakan turunan khusus.

Ini adalah bug syn . Saya merilis 0.8.2 dengan perbaikan jadi cargo update untuk mengambilnya.

Saya tidak tahu tentang yang kedua, akan serahkan ke @alexcrichton.

@jodohgratis

  • Deserialize dan Serialize tidak muncul di bagian "Implementasi Sifat" untuk tipe yang menggunakan turunan kustom.

Kedengarannya cukup mencurigakan! Sepertinya ini adalah bug di rustdoc, tapi mungkin bukan yang baru. Misalnya, rustdoc tidak menunjukkan ini dengan implementasi Copy :

#[derive(Clone)]           
pub struct Point {         
    x: i32,                
    y: i32,                
}                          

const _FOO: () = {         
    impl Copy for Point {} 
    ()                     
};                         

Pola itu digunakan oleh serde-derive untuk menghasilkan impl. @dtolnay apakah serde_macros memiliki bentuk generasi yang sama juga? Apakah mudah untuk beralih dari sekarang untuk menyelesaikan masalah dokumentasi?

@jimmycuadra ingin membuka bug terpisah untuk melacak masalah rustdoc tentang itu?

@dtolnay apakah serde_macros memiliki bentuk pembangkitan yang sama?

Iya.

Apakah mudah untuk beralih dari sekarang untuk menyelesaikan masalah dokumentasi?

Tidak. Sejauh yang saya tahu, ini adalah satu-satunya solusi yang memenuhi kedua kendala berikut:

  • Harus mendukung serde bukan di root peti, yaitu bukan ::serde . Ini umum terjadi ketika orang meletakkan serde impls di belakang flag fitur dalam modul terpisah.
  • Harus dapat menggunakan tipe pribadi dari modul sekitarnya.

Lihat https://github.com/serde-rs/serde/issues/159 untuk detailnya.

@dtolnay ah ok, jadi untuk memperjelas, @jimmycuadra ini bukan regresi dari sebelumnya kan?

Pembaruan: Tidak cukup selesai dengan port, tetapi hampir sampai. Tidak ada masalah selain hal-hal yang telah saya laporkan atau batasan kecil yang kami tahu akan kami temui. Mungkin aman untuk mencentang kotak "diesel works", kami akan merilisnya besok di Macros 1.1 jika itu tidak terjadi malam ini.

Bagi mereka yang mengikuti, saya telah membuka # 36945 untuk mengganti nama rustc_macro menjadi proc_macro hampir di mana-mana yang tampaknya merupakan konsensus tentang nama peti ini.

Sepertinya implementasi sifat yang hilang di rustdoc (yang sekarang dilacak di masalah lain) tidak spesifik untuk Macro 1.1. Aman untuk diabaikan.

Saya mencoba mem-port perpustakaan mockers dari plugin compiler ke makro 1.1 dan mendapatkan "error: atribut turunan kustom hanya dapat diterapkan ke item struct / enum". Dengan plugin kompilator dimungkinkan (meskipun agak aneh) untuk menggunakan "turunan" pada sifat-sifat. Apakah saya kurang beruntung di sini?

@kriomant menarik! Saya pikir itu mungkin benar-benar bug dalam turunan khusus hari ini, karena saya tidak yakin itu dimaksudkan untuk memungkinkan #[derive] untuk diterapkan ke suatu sifat ...

Saya pikir untuk saat ini untuk menjadi konservatif kita cenderung untuk belum _stabilize_ derive-on-traits, tapi mungkin kita bisa menambahkan fitur yang tidak stabil untuk itu. Pikiran @ rust-lang / lang?

@alexcrichton Apa perbedaan antara traits dan struct dari aspek custom_derive?

@KalitaAlex tidak ada, ini adalah batasan buatan untuk mencocokkan implementasi derive

@alexcrichton Bisakah kita memberikan dukungan untuk ciri-ciri?

Seperti yang saya sebutkan di atas , kemungkinan bug yang berasal dari kebiasaan pernah diizinkan pada sifat, dan ini tidak ditentukan di RFC, jadi lebih banyak diskusi perlu dilakukan sebelum memperluas. Bagaimanapun, dukungan untuk derive-on-trait sepertinya tidak akan distabilkan pada lintasan pertama, tetapi kami dapat mempertimbangkan gerbang fitur terpisah.

Saya pribadi tidak ingin menambahkan derive-on-trait karena hal itu tampaknya lebih sesuai dengan wilayah atribut khusus daripada mendapatkan dirinya sendiri. (mis. bertentangan dengan semangat #[derive] seperti yang aslinya dibuat)

Saya lebih suka turunan kustom mengikuti aturan yang sama persis dengan turunan biasa. Kami _might_ ingin mengubahnya nanti, tetapi saya pikir itu harus RFC'ed (Saya juga cukup dingin pada idenya, tbh, tapi saya bisa mengubah pikiran saya dengan kasus penggunaan yang menarik dan penanganan yang layak dari berbagai kasus tepi ).

@alexrichard

Saya pikir untuk saat ini untuk menjadi konservatif kita cenderung belum menstabilkan turunan-pada-sifat dulu, tapi kita mungkin bisa menambahkan fitur yang tidak stabil untuk itu. Pikiran @ rust-lang / lang?

👍 dari saya.

Oke, fitur tidak stabil itu bagus, tetapi itu berarti perpustakaan saya tidak akan berfungsi pada stabil (tanpa pembuatan kode). Dan sintaks macro!(…) juga tidak tercakup oleh "makro 1.1", apakah saya benar?

Saya mengunjungi kembali proc makro RFC dan rencananya adalah bahwa peti makro harus mendeklarasikan dirinya sendiri #[cfg(macro)] . Kami tidak memerlukan ini untuk makro 1.1, tetapi kami memerlukan jenis peti khusus. Kedua mekanisme tersebut agak ortogonal - yang pertama menjelaskan fase kompilasi, yang kedua adalah jenis peti. Tetapi mereka juga agak tumpang tindih - jika yang terakhir menyiratkan yang pertama. Yang pertama juga berskala untuk mendeklarasikan makro proc dan fungsi non-makro dalam peti yang sama, sedangkan yang terakhir tidak.

Saya tidak yakin apakah kita perlu mengubah sesuatu di sini, kita mungkin bisa meretas proposal makro proc ke kompatibilitas mundur (ini sengaja menjelaskan mekanisme memuat makro dan dengan demikian jenis peti). Tetapi sesuatu untuk direnungkan selama periode stabilisasi.

@kriomant benar, ya

@nrc sekarang ini sebenarnya didefinisikan sebagai cfg(rustc_macro) (meskipun akan segera berubah menjadi proc_macro ). Kami tidak memerlukannya, tidak, tapi saya pikir itu akan diperlukan untuk konsep menghubungkan ke peti waktu kompilasi dan juga saat runtime? Artinya, kami akan mengkompilasi kotak proc-macro dua kali: sekali dengan jenis peti proc-macro dan sekali dengan jenis peti rlib , dan yang terakhir tidak akan menautkan ke libsyntax atau semacamnya bahwa.

Untuk saat ini, meskipun tampaknya baik-baik saja untuk tidak _require_, meskipun saya rasa itu berarti di kemudian hari Anda harus ikut serta dalam dukungan runtime? (menyusun peti dua kali)

@alexcrichton Bisakah #[proc_macro_derive] menyiratkannya? Dengan cara yang sama #[test] berarti #[cfg(test)] .
(Tidak berarti kita harus menambahkannya sekarang, hanya saja kasus terburuk jika kita menambahkan cfg adalah peringatan item yang tidak terpakai.)

@eddyb Bagaimana dengan extern crate proc_macro; ? Saya merasa seperti itu akan rusak juga.

@mystor Ini hanya peti biasa dengan banyak jenis dan implan.

Bukankah itu juga menautkan ke libsyntax , yang berarti bahwa jika sebuah peti menggunakan peti proc_macro dan ingin mengkompilasi silang, ia juga harus mengkompilasi silang sintaks?

@eddyb yes #[proc_macro_derive] dapat diabaikan secara otomatis, tetapi masalahnya adalah bahwa kita _also_ perlu mengabaikan semua yang dicapai oleh fungsi tersebut secara transitif (seperti extern crate proc_macro ). Meskipun "hanya peti", ia memiliki implikasi runtime yang parah (penautan dinamis, tidak tersedia untuk target yang dikompilasi silang, dll).

Perhatikan bahwa tes memiliki #[cfg(test)] , jadi masuk akal bagi saya bahwa kami masih memberikan #[cfg(proc_macro)] untuk tujuan yang pada dasarnya sama.

@alexcrichton _Ideally_, peti tidak akan memiliki lebih dari apa yang diekspor dan tidak memerlukan tautan dinamis atau semacamnya, hanya libstd . Namun, saya bisa melihatnya menyebabkan masalah di #![no_std] kasing.

Kedengarannya seperti kita harus melakukan kompilasi ganda dari awal jika kita ingin menangkap semuanya: dis ditunjuk :.

EDIT : Tunggu, apa yang saya pikirkan? Ini memerlukan jenis peti khusus, kompilasi ganda berlaku untuk _regular_ peti yang _also_ mengekspor prosedural makro / atribut / derives / dll. Jadi tidak relevan untuk saat ini.
Tapi setidaknya kita bisa memperkenalkan #[cfg(proc_macro)] yang selalu ditetapkan untuk jenis peti baru.

🔔 Fitur ini memasuki periode komentar terakhir dengan maksud untuk menjadi stabil di akhir siklus rilis ini! 🔔

Alasan untuk mempertimbangkan stabilisasi sekarang adalah:

  • Permukaan API dari fitur ini, menurut desain, _extremely_ konservatif. Pada saat yang sama, ini kompatibel dengan rencana de facto untuk makro prosedural
  • Fitur ini dengan cepat digunakan secara luas melalui Serde, dan segera Diesel - meskipun, terutama, penggunaan yang meluas sebagian besar sebagai _client_ dari turunan kustom.
  • Masuk ke FCP sekarang memberi kami waktu tiga bulan sebelum fitur benar-benar dikirim dalam kondisi stabil, yang seharusnya cukup waktu untuk mengatasi masalah yang tersisa.

Perhatikan bahwa namanya bergeser menjadi proc_macro , berdasarkan konsensus sebelumnya di utas ini. Kami dapat terus berbagi sepeda ini dan poin bagus lainnya selama sisa siklus rilis ini.

Mengompilasi peti dua kali terdengar sangat kasar: pelingkupan tidak akan berfungsi dengan benar. Sesuatu seperti extern! crate needed_for_my_inline_proc_macros; adalah solusi yang jauh lebih baik.

@aturon apa, bukankah orang baru mulai menggunakan hari ini yang lalu?

@ Ericson2314 Sudah sedikit lebih lama dari itu, tetapi seperti yang dijelaskan oleh komentar FCP, waktu _minimum_ sebelum pengiriman ke saluran stabil adalah tiga bulan dari sekarang, yang kami rasa lebih dari cukup untuk antarmuka yang sangat sempit ini.

Perhatikan bahwa FCP itu sendiri adalah urusan selama 6 minggu yang tidak berarti kami bahkan akan meletakkannya di jalur menuju stabilisasi. Namun setidaknya dengan memulai proses sekarang, kami menciptakan peluang untuk mengirimkan fitur ini dalam 3 bulan, dengan asumsi tidak ada masalah yang ditemukan sebelumnya.

Ah ok. Saya ingat bagian 3 bulan, tetapi lupa lokasi FCP dalam 3 mulut itu --- jangan sampai itu 3 bulan dari 22 Agustus. Lupakan waktunya kalau begitu.

Saya pikir masalah pentahapan yang saya kemukakan berdampak bahkan pada bagian kecil dari kisah makro proc ini, jadi saya ingin melihat hal itu ditangani.

@alexcrichton , rustc_copy_clone_marker dan rustc_attrs lainnya masih menggigit kita. Lihat https://github.com/serde-rs/serde/issues/577.

#[derive(Copy, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct MyStruct {
    value: i64,
}

Solusinya adalah menukar pesanan tetapi saya pikir saya akan memeriksa apakah ini dapat diperbaiki.

#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone)]
pub struct MyStruct {
    value: i64,
}

Saya telah menyelesaikan porting https://github.com/sfackler/rust-postgres-derive ke makro 1.1 Itu relatif tidak merepotkan, tetapi menangani atribut yang dibaca oleh beberapa turunan sangat merepotkan. Serde mengalami hal yang sama, tetapi membutuhkan sekumpulan logika aneh untuk mengembangkan keduanya secara bersamaan: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-internals/ src / lib.rs # L26

Ini seharusnya tidak menghalangi stabilisasi tetapi saya pikir ini adalah masalah ergonomis yang relatif besar di sisi kepenulisan yang mungkin harus segera kita tangani.

Saya merilis quote 0.3.0 dengan dukungan untuk pengulangan gaya macro_rules! (terima kasih @nrc atas dorongannya). Jika Anda mengembangkan makro proc tanpa quasi / syntex, Anda mungkin menginginkan ini. Contoh penggunaan dari postgres-derive:

pub fn enum_body(name: &str, variants: &[Variant]) -> Tokens {
    let num_variants = variants.len();
    let variant_names = variants.iter().map(|v| &v.name);

    quote! {
        if type_.name() != #name {
            return false;
        }

        match *type_.kind() {
            ::postgres::types::Kind::Enum(ref variants) => {
                if variants.len() != #num_variants {
                    return false;
                }

                variants.iter().all(|v| {
                    match &**v {
                        #(                           // \
                            #variant_names => true,  //  |----- new feature
                        )*                           // /
                        _ => false,
                    }
                })
            }
            _ => false,
        }
    }
}

Saya penasaran, mengapa # dan bukan $ ?

EDIT : Selalu berpikir sintaks kutipan akan menjadi ${...} tapi mungkin saya terlalu terikat pada literal template ES6 bahkan jika sudah beberapa tahun. \{...} juga berfungsi meskipun akan berguna di tempat lain.
Tidak ada tanda kurung yang tampaknya sulit dikenali, tetapi saya tidak perlu khawatir.

Saya penasaran, mengapa # dan bukan $ ?

Karena ini makro macro_rules dan saya tidak bisa melakukan apapun yang saya inginkan: smile : $v dicocokkan sebagai token tunggal dan tidak ada cara untuk beralih dari $v menjadi menggunakan variabel v . Sebaliknya #v adalah dua token jadi saya bisa mencocokkannya secara terpisah dan melakukan sesuatu dengan ident.

macro_rules! demo {
    ($tt:tt) => {};
}

fn main() {
    demo!($v);
}

Tunggu semua selesai di macro_rules ?! Forgot macro 1.1 hanya diturunkan sesaat. Yang mengatakan, $x menjadi satu token adalah IMO cacat desain. cc

@bayu_joo

@alexcrichton the rustc_copy_clone_marker dan rustc_attrs lainnya masih menggigit kita. Lihat serde-rs / serde # 577.

Ya ampun, sepertinya buruk! Saya akan memperbarui daftar di atas. Pikiran @nrc atau @jseyfried tentang bagaimana kita dapat menangani ini? Saya tidak terlalu paham dengan urutan perluasan dari semuanya, tetapi mungkin ketika satu atribut #[derive] sedang diperluas, ia dapat mencoba untuk menyeruput semua yang akan datang dan melakukannya terlebih dahulu?

@tokopedia

penanganan atribut yang dibaca oleh beberapa turunan merupakan masalah besar

Saya tidak yakin saya cukup mengikuti contoh yang Anda tautkan, bisakah Anda menjelaskannya?

@alexcrichton Ambil contoh

#[derive(ToSql, FromSql)]
enum Foo {
    #[postgres(name = "bar")]
    Bar
}

Atribut #[postgres] digunakan untuk menyesuaikan bagaimana implementasi ToSql dan FromSql dihasilkan. Ini perlu dihilangkan sebelum keluaran akhir karena itu adalah atribut yang tidak diketahui, tetapi implementasi ToSql dan FromSql dijalankan secara terpisah. Jika Anda melakukannya dengan cara yang naif dengan hanya membuat implementasi dan kemudian menghapus atributnya, penyesuaian kedua akan hilang.

serde-derive dan postgres-derive hack di sekitar ini sekarang dengan menerapkan kedua impls turunan maju ke fungsi yang sama yang menghasilkan keduanya sekaligus. Kita harus memasang kembali #[derive] untuk yang saat ini sedang dipanggil sejak kompilator menghapusnya, dan kemudian mengirimkannya untuk diperluas: https://github.com/sfackler/rust-postgres-derive/blob /master/postgres-derive/src/lib.rs#L18

@sfackler I _think_ Anda juga dapat melakukannya dengan menghapus atribut tambahan hanya jika tidak ada turunan dari pelaksana yang sama yang tersisa. Cara Anda mungkin lebih baik _shrug_.

@sfackler ah ok, masuk akal. Terima kasih!

Saya ingin tahu apakah logika itu mungkin rapuh. Misalnya jika Anda memperluas ToSql bagaimana cara mendeteksi perluasan masa depan FromSql ? Mungkinkah di belakang #[cfg] seperti @dtolnay yang disebutkan di atas mungkin? Jadi tidak mungkin untuk mendeteksi dalam semua kasus?

@alexcrichton Ya, ini rapuh, itulah sebabnya solusi nyata tampaknya penting.

Bukankah #[cfg] dan #[cfg_attr] akan diproses sebelum diperoleh?

Karena api makro 1.1 berfungsi pada string, saya hanya dapat membayangkan tiga solusi:

  1. biarkan apa adanya dan tunggu makro 2.0
  2. izinkan atribut yang tidak digunakan dalam turunan khusus
  3. memperluas api makro 1.1 dengan mengizinkan turunan khusus untuk mendorong nama atribut ke daftar putih tertentu untuk item saat ini

Setiap opsi memiliki kelebihan / kekurangannya masing-masing.
con 1: solusi rapuh untuk sementara waktu
con 2: beberapa atribut yang tidak digunakan tidak akan terdeteksi
con 3: lebih banyak permukaan api untuk solusi sementara

Saya telah mempertimbangkan untuk membungkus atribut dalam #[used(...)] untuk menyimpannya dan memasukkannya ke daftar putih pada saat yang sama, tetapi itu cukup konyol dan terlalu stabil.

@alexrichard

ketika satu atribut #[derive] sedang diperluas itu dapat mencoba untuk menyeruput semua atribut yang akan datang

Aku suka ide ini. Karena #[cfg] s dan dan #[cfg_attr] s diproses sebelum #[derive] s, #[cfg] -guarded #[derive] s tidak menjadi masalah untuk pendekatan ini (atau untuk solusi analog @sfackler untuk masalah pengupasan atribut).

Pendekatan ini akan membuat solusi @sfackler sedikit lebih sederhana, karena turunan relevan lainnya hanya dapat berada di atribut terakhir (menurut saya saran @eddyb juga akan menyederhanakan banyak hal).

Salah satu kemungkinan untuk masalah atribut kontrol adalah menambahkan callback pasca-perluasan, agak mirip dengan syntex: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-codegen/src/lib. rs # L23 -L50

Implikasi turunan Anda bisa independen dan tidak menghapus atribut kontrol, dan Anda bisa mendaftarkan pass yang dijalankan setelah semua perluasan selesai dilakukan untuk pembersihan.

@dtolnay kekhawatiran Anda sebelumnya tentang beberapa atribut #[derive] harus segera diperbaiki berkat @jseyfried

Saya bermain dengan ini menggunakan paket syn dan quote , dan memiliki pengalaman yang sangat positif. Ini akan menjadi fitur hebat saat stabil.

Masalah dengan atribut saat ini menggigit impl saya, menjadi downstream dari serde s derive logic.

Mengabaikan atribut yang tidak digunakan dalam kode turunan, atau mengizinkan perluasan terlambat yang berjalan setelah turunan reguler untuk menghapusnya tampak seperti solusi yang masuk akal bagi saya.

Akan ideal jika makro tidak memiliki opsi untuk mengubah aliran token asli, sepertinya fitur berbahaya yang dapat diandalkan di alam liar antara sekarang dan makro 2.0 dan oleh karena itu sulit untuk dihapus nanti.

Jadi saya memiliki titik koma yang hilang di jalur tipe di dalam fungsi generik, dan mendapat pesan kesalahan berikut dari rustc. Saya bertanya-tanya apakah lebih banyak informasi dapat diberikan, seperti makro mana yang menyebabkan kesalahan lex, token mana yang menyebabkannya, dll.

error: custom derive attribute panicked
  --> src/simple.rs:69:1
   |
69 | struct C(u64);
   | ^^^^^^^^^^^^^^
   |
   = help: message: Failure parsing derived impl: LexError { _inner: () }

Saya membuat kasus uji untuk LexError, tampaknya terjadi pada 13 Oktober malam (rustup tidak memiliki pembaruan) https://github.com/keeperofdakeys/proc_macro_derive_test.

@ rust-lang / lang Saya rasa kita perlu membahas masalah apakah turives dapat memodifikasi item yang diberi anotasi. Meskipun ini adalah solusi sederhana untuk mengizinkannya, tampaknya bertentangan dengan harapan pengguna dan tampaknya cukup tidak populer. Saya masih lebih suka solusi saat ini - kesederhanaannya bagus, dan tidak ada alternatif yang diusulkan menurut saya lebih baik.

Kita membutuhkan solusi untuk mengontrol atribut jika item tersebut dapat dimodifikasi. Saya tidak berpikir saya merasa sangat kuat tentang menjaga kemampuan untuk menurunkan untuk memodifikasi item, selama (di masa depan) makro atribut non-derive bisa.

Seseorang harus mendefinisikan "memodifikasi". Apakah kita berbicara tentang memodifikasi anggota struct, atau hanya kita berbicara tentang pengupasan atribut untuk mencegah kompilator mengeluh tentang atribut yang tidak diketahui?

Jika kita berbicara tentang memodifikasi anggota struct, menurut pendapat saya solusi terbaik mungkin adalah mengizinkan hanya satu makro pemodifikasi struct pada setiap struct yang diperluas sebelum yang lain. Itu akan memiliki perilaku yang terdefinisi dengan baik dan (menurut saya) diharapkan.

Jika kita hanya berbicara tentang pengupasan atribut, mungkin seharusnya ada cara untuk mengintegrasikan atribut dalam mekanisme makro itu sendiri daripada menyerahkannya pada kebijaksanaan makro.

Intuisi saya tentang bagaimana derive berperilaku adalah menambahkan impls. Untuk pengguna akhir, derives seharusnya menambahkan impls dan tidak melakukan apa pun. Secara khusus, mereka tidak boleh memodifikasi struct dengan cara yang akan menjadi masalah bagi derivers lain, dan oleh karena itu mereka harus independen urutan. Apakah kita setuju bahwa beginilah seharusnya perilaku sebuah derive, atau apakah menurut orang derive juga harus mampu melakukan transformasi pada tipe yang melekat?

Jika kita setuju tentang itu, pertanyaannya menjadi - apakah kita ingin antarmuka yang kita tunjukkan untuk memberlakukan ini, atau kita ingin membiarkannya mendapatkan penulis untuk menegakkan ini? Di sini, tampaknya ada dua masalah yang berlawanan:

  • Menegakkan kontrak perilaku turunan jelas lebih seperti solusi Rust bagi saya.
  • Mendapatkan serde dan deisel untuk mengatasi kendala itu menghadirkan banyak tantangan.

Dan tentu saja bagaimana atribut lain dapat memodifikasi struct tampaknya tidak terkait dengan bagaimana turunan, khususnya, harus berperilaku.

Perlu dicatat bahwa kemampuan untuk menghapus item yang dianotasi memungkinkan sistem saat ini untuk mengisi banyak peran yang sebaliknya tidak akan dilakukannya.

@sgrif Bisakah Anda menjelaskan bagaimana deisel menggunakan turunan? Saya merasa seperti saya mengerti bagaimana serde menggunakan kemampuannya untuk memodifikasi struct (untuk menghapus atribut yang menginformasikan makro untuk menghilangkan atau mengganti nama bidang dalam sifat serialisasi), tetapi mungkin deisel menggunakan ini untuk melakukan sesuatu yang lain. Saat Anda mengatakan "hapus item yang diberi anotasi", sepertinya Anda melakukan transformasi yang cukup radikal pada item tersebut.

@withoutboats 90% itu lebih atau kurang dari yang Anda harapkan. Kami tidak menyentuh item yang disediakan oleh pengguna. Kami menghapus item beranotasi untuk meretas makro bang ke dalam sistem. https://github.com/diesel-rs/diesel/blob/master/diesel/src/macros/macros_from_codegen.rs#L12 -L18. Di luar itu, satu-satunya saat kami menyentuh apa pun di aliran token input adalah menghapus anotasi. https://github.com/diesel-rs/diesel/blob/master/diesel_codegen/src/lib.rs#L109 -L120

@sgrif saya juga telah menyalahgunakan custom

Namun, saya dapat memahami argumen untuk mempertahankan derive sebagai makro sederhana.

Perspektif saya adalah bahwa tujuan Macro 1.1 adalah menjadi sefleksibel mungkin untuk memenuhi kebutuhan sebanyak mungkin, dengan beban pemeliharaan yang rendah sehingga dapat distabilkan dengan cepat dan bertindak sebagai stopgap hingga makro 2.0. Desain saat ini sangat cocok dengan peran itu menurut saya.

Jika kita berbicara tentang sesuatu yang dimaksudkan untuk mengisi peran ini secara permanen, saya akan jauh lebih menentangnya

Mungkin saya salah, tapi pembacaan saya terhadap RFC adalah bahwa hal ini dimaksudkan untuk menjadi dasar perilaku turunan secara permanen. Artinya, lebih banyak metode akan ditambahkan ke TokenStream di masa mendatang, tetapi makro yang diturunkan akan menggunakan API ini, yang saat ini memungkinkan mereka untuk melakukan mutasi sewenang-wenang pada item yang dianotasi (dan kemampuan ini diperlukan untuk kasus penggunaan deisel ).

Saya merasa sangat negatif tentang mengizinkan turunan untuk melakukan ini secara permanen. Jika kami menerima bahwa ini adalah sistem yang akan ditinggalkan, bersama dengan macro_rules makro, di beberapa titik di masa mendatang, dan di bawah makro 2.0 API turunan berbeda yang lebih terkendali akan lebih disukai, saya lebih nyaman dengannya.

Tampaknya tujuannya adalah untuk mendukung dekorator sebagai transformer yang bisa melakukan apa saja.
Dan dapatkan ekspos sebagai dekorator sederhana tanpa masukan di atribut.

@withoutboats : serde mendukung sejumlah atribut untuk memodifikasi bagaimana impls dibuat, jadi kami pasti membutuhkan kemampuan untuk menghapusnya atau mengabaikannya setelah kami memprosesnya. Jika itu membantu, entah bagaimana kami bisa memberikan daftar atribut ke kompiler yang harus dihapus, daripada kami ingin melakukannya sendiri.

@eddyb Saya mendukung dekorasi yang dapat melakukan apa saja, tetapi tidak mendukung dekorasi yang tidak terkendali (dalam jangka panjang).

@erickt Benar. Saya pikir dalam jangka panjang solusi yang ideal adalah atribut tersebut didaftarkan sebagai atribut khusus tanpa operasi, alih-alih deriver bertanggung jawab untuk menghapusnya. Tapi itu tidak mungkin dilakukan dalam jangka pendek.

Saya pikir dalam jangka panjang solusi yang ideal adalah atribut tersebut didaftarkan sebagai atribut khusus tanpa operasi, alih-alih deriver bertanggung jawab untuk menghapusnya. Tapi itu tidak mungkin dilakukan dalam jangka pendek.

Saya tidak terbiasa dengan internal kompilator yang membuat ini tidak mungkin dalam jangka pendek, tetapi apakah mungkin untuk plugin turunan khusus untuk menyajikan daftar atribut khusus yang ingin dihilangkannya dan kemudian menolak transformasi lain pada atribut di barang asli?

Saya juga memperhatikan bahwa Diesel tidak mengikuti pendekatan Serde untuk memiliki semua atribut khusus di bawah satu nama (misalnya #[serde(rename = "name")] sebagai lawan dari Diesel #[table_name = "name"] .) Apakah itu akan menyederhanakan implementasi jika hanya satu nama atribut khusus terdaftar sebagai ganti daftar?

Salah satu kemungkinan untuk masalah atribut kontrol adalah menambahkan callback pasca-perluasan, agak mirip dengan syntex: https://github.com/sfackler/rust-postgres-derive/blob/master/postgres-derive-codegen/src/lib. rs # L23 -L50

Implikasi turunan Anda bisa independen dan tidak menghapus atribut kontrol, dan Anda bisa mendaftarkan pass yang dijalankan setelah semua perluasan selesai dilakukan untuk pembersihan.

Saya menerapkan callback pasca-perluasan untuk Macro 1.1 di post-expansion . Implikasi turunan Anda bisa independen dan tidak menghapus atribut kontrol, dan Anda bisa mendaftarkan pass yang berjalan setelah semua perluasan dilakukan untuk menghapus atribut.

Kami membahas masalah modifikasi / pemesanan hari ini di rapat tim lang. Ada keinginan untuk memenuhi harapan pengguna, dan untuk kesederhanaan (dalam hal desain, tidak memiliki terlalu banyak peretasan berlapis di bagian atas, dan tidak terlalu tunduk pada kesalahan yang sulit diuraikan / debug). Tercatat bahwa meskipun modifikasi data target mengejutkan, hal ini bukannya tidak aman. Juga dirasakan bahwa kita _mungkin_ cenderung menuju kemurnian demi kemurniannya sendiri, daripada untuk alasan yang termotivasi dengan baik.

Pada akhirnya, kami memutuskan bahwa turunan yang tidak mengubah sumber mungkin lebih baik dan kami harus mengubah ke model itu. Itu mungkin berarti memperpanjang periode FCP. Kami tidak berpikir harus ada mekanisme khusus untuk menangani atribut stripping. Sebaliknya, cara kompilator menangani atribut harus memungkinkan atribut yang digunakan oleh makro untuk tetap berada dalam program. RFC 1755 harus mempertimbangkan hal ini.

Ini akan menunda stabilisasi beberapa pengguna turunan khusus. Namun, kami yakin bahwa sebagian besar penggunaan turunan (terutama yang membuat pengguna tidak menggunakan toolchain yang stabil) tidak menggunakan atribut, jadi misalnya, sebagian besar pengguna Serde akan dapat segera pindah ke stabil. Mereka yang membutuhkan atribut, akan membutuhkan beberapa siklus lebih lama _tapi itu tidak akan mempengaruhi case_ umum.

cc @alexcrichton , @dtolnay , @sgrif , @erickt - pemikiran?

Atribut mungkin lebih umum digunakan dengan Serde dan Diesel daripada yang Anda sarankan. Hanya dari pengalaman saya sendiri, saya cukup yakin semua program saya yang menggunakan atribut penggunaan Serde. Dengan Diesel saya pasti menggunakan atribut, dan saya pikir itu diperlukan untuk memberi tahu diesel_codegen tabel database mana yang memetakan ke struct.

Yang mengatakan, tidak membiarkan custom menurunkan mutasi struct sepertinya pilihan yang tepat bagi saya. Itu hanya menyederhanakan semuanya, mencegah banyak kasus tepi yang aneh. Lebih penting untuk melakukannya dengan benar daripada menyelesaikannya dengan cepat, jadi jika fitur harus tetap tidak stabil sedikit lebih lama, itu juga tampak bagus.

Tercatat bahwa meskipun modifikasi data target mengejutkan, hal ini bukannya tidak aman.

Itu tidak tidak aman, kecuali jika Anda mendapatkan sifat yang tidak aman.

Menurut pendapat saya, salah satu kasus penggunaan custom derives adalah untuk mengimplementasikan dengan cara yang aman, ciri-ciri yang ditandai sebagai tidak aman, seperti misalnya sifat yang harus menggambarkan dengan tepat tata letak anggota struct tempat penerapannya.

Peti asn1 saya juga memerlukan atribut untuk apa pun kecuali penggunaan yang sepele, jadi saya secara efektif harus menunggu sampai atribut khusus mendarat.

Apakah sebaiknya Anda membagi atribut khusus rfc menjadi dua?

  1. Sebuah rfc untuk memberikan notasi dan namespacing untuk atribut khusus secara umum (mengizinkan atribut tanpa operasi dalam keadaan stabil).
  2. Rfc untuk cara menentukan, dan semantik seputar menjalankan makro atribut khusus.

Makro atribut khusus tampak seperti sesuatu yang akan membutuhkan banyak hal jika menyempurnakan. Jadi, membagi rfc menjadi dua dapat memberikan atribut stabil lebih cepat untuk paket turunan kustom.

Ini juga berarti atribut diberi spasi nama, dan disengaja (mis. Peti eksternal diperlukan untuk menggunakan atribut untuk peti). Saya dapat melihat ini menjadi hal yang baik untuk mencegah dua makro turunan khusus menggunakan nama atribut yang sama. Harapan yang baik di sini akan menggunakan nama peti dari sifat tersebut untuk atributnya.

Apakah ada alasan mengapa penerapan ini tidak mencakup makro name! ? Sederhana TokenStream masuk, TokenStream keluar untuk makro biasa akan terbukti sangat berguna dalam hama di mana waktu kompilasi untuk tata bahasa kompleks melebihi 30-an.

@dragostis Mengutip ringkasan rfc:

Ekstrak bagian yang sangat kecil dari sistem makro prosedural saat ini di kompiler, cukup untuk mendapatkan fitur dasar seperti custom derive berfungsi, agar akhirnya memiliki API yang stabil. Pastikan bahwa fitur ini tidak akan menimbulkan beban pemeliharaan pada compiler tetapi juga jangan mencoba menyediakan fitur yang cukup untuk "sistem makro yang sempurna" pada saat yang bersamaan. Secara keseluruhan, ini harus dianggap sebagai langkah tambahan menuju "makro 2.0" resmi.

Atau dalam istilah yang lebih praktis, akan membutuhkan banyak desain dan pengujian untuk mendapatkan sistem makro prosedural yang berfungsi dengan baik, seperti halnya sistem closure yang sedang kita bangun. Peti seperti serde dan diesel memiliki masalah kegunaan yang serius tanpa fitur turunan khusus yang tepat, jadi mari kita buat ukuran sementara untuk memperbaikinya sekarang - yang juga membantu perkakas dan pengalaman dengan kemungkinan sistem makro prosedural. Peti syn dan quote adalah contoh yang bagus untuk ini.

@keeperofdakeys Ya, mengerti. Saya tidak meminta implementasi makro 2.0, saya hanya ingin tahu apakah ada beban yang terlibat untuk menambahkan atribut lain, katakanlah proc_macro yang merupakan implementasi minimal yang hanya mencerminkan desain turunan hanya untuk makro biasa. Contoh dalam hama hanya akan mendapatkan parser untuk struct hanya itu tidak akan memperolehnya dari struct itu sendiri tetapi dari tata bahasa sederhana yang ditentukan antara {} . Saya harap saya tidak menggali diskusi mati!

@dragostis Saya mengemukakan kemungkinan ini di internal beberapa waktu lalu. Anda dapat membaca tanggapannya di sana: https://internals.rust-lang.org/t/pre-rfc-extend-macros-1-1-to-support-foo-style-macros/3921

@rc

Juga dirasakan bahwa kita mungkin cenderung menuju kemurnian demi kemurniannya sendiri, daripada untuk alasan yang termotivasi dengan baik.

Satu catatan tentang ini: dalam banyak kasus di mana kita harus membuat keputusan yang sulit, seperti parametrikitas dan spesialisasi, secara statis memastikan gagasan yang relevan tentang "kemurnian" akan sulit / tidak mungkin. Namun dalam kasus ini, sebenarnya sangat mudah untuk menjaminnya dengan konstruksi, dan menghilangkan signifikansi urutan turunan tampaknya seperti penyederhanaan yang cukup kuat bagi pengguna.

Sejauh stabilitas berjalan, kami dapat mempertimbangkan untuk menambahkan mekanisme yang tidak stabil untuk mengabaikan atribut sekarang, yang dalam praktiknya akan stabil (karena API tidak akan rentan terhadap kerusakan) meskipun masih memerlukan penggunaan setiap malam. Kami bahkan dapat mempertimbangkan untuk menstabilkan saluran samping seperti itu, dengan rencana untuk menghentikannya demi mekanisme yang lebih umum jika tersedia. Atau kita dapat mempertimbangkan saran @keeperofdakeys dan mendorong dengan cepat bagian dari solusi atribut umum yang akan menangani masalah langsung.

Dari perspektif saya, penting bahwa tidak satu pun dari masalah ini yang secara signifikan memblokir makro 1.1 agar tidak dapat digunakan secara luas untuk Serde dan setidaknya stabil "de facto" (yaitu, tidak rusak).

@ syrif

Jika kita berbicara tentang sesuatu yang dimaksudkan untuk mengisi peran ini secara permanen, saya akan jauh lebih menentangnya

Saya ingin menggemakan @withoutboats dan mengklarifikasi bahwa maksud saat ini adalah agar permukaan API makro 1.1 tetap apa adanya bahkan ketika kami mengirimkan makro 2.0. Namun, itu adalah sesuatu yang dapat kami pertimbangkan kembali - kami dapat menganggapnya lebih seperti macro_rules hari ini, dengan maksud untuk tidak digunakan lagi setelah sistem final diterapkan.

Kesan saya tentang penggunaan atribut pada custom derive mirip dengan kesan @jimmycuadra , yaitu cukup umum digunakan. Saya percaya (tetapi perbaiki saya jika saya salah) bahwa atribut khusus sangat penting untuk sejumlah kasus penggunaan diesel.

Dalam hal ini, saya tidak 100% yakin cara terbaik ke depan tentang atribut tersebut. Akan memalukan untuk menstabilkan makro 1.1 tetapi masih menyisakan sejumlah besar pengguna pada malam hari (meskipun itu "defacto stable" setiap malam) karena itu agak mengalahkan tujuan stabilisasi cepat makro 1.1 di tempat pertama. Dengan kata lain, jika kita tidak menarik sebagian besar makro 1.1 pengguna _ sebenarnya_ ke Rust stabil, menstabilkan makro 1.1 saya pikir bisa menunggu.

Satu hal yang mengganggu saya adalah bagaimana menurut kami atribut khusus akan terlihat dalam jangka panjang dan rasionalisasi dengan sistem makro 1.1 saat ini. Dalam RFC saat ini untuk atribut khusus, peti harus mendeklarasikan ruang nama atribut yang masuk daftar putih di bagian atas peti. Ini berarti bahwa menggunakan atribut khusus dalam turunan khusus sangat berbeda dengan menggunakan atribut khusus di tempat lain. Putuskan sambungan itu membuat saya khawatir dalam hal perspektif ergonomis dan "paling tidak mengejutkan", meskipun saya juga melihatnya sebagai fungsi pemaksaan untuk mengubah RFC (misalnya jika saya menautkan ke peti serde mungkin itu dapat secara otomatis memasukkan atribut serde ke daftar putih ).

Secara keseluruhan saya pribadi akan baik-baik saja bergerak maju dengan stabilisasi lengkap sistem makro 1.1 seperti sekarang ini. Atau dengan kata lain, stabilkan fakta bahwa fungsi perluasan yang diperoleh secara khusus menerima struct dan kemudian harus mengembalikan struct tersebut jika ingin dipertahankan.

Saya cenderung beralih dari rustc-serialize ke pembuatan kode serde _because_ serde mendukung atribut kontrol.

Kita bisa memperluas makro 1.1 untuk mendukung argumen ke atribut turunan itu sendiri. Secara umum hal itu tampak menyenangkan, dan dapat disalahgunakan untuk mengatasi kekurangan atribut kontrol dalam jangka pendek.

Ditto @jimmycuadra dan @sfackler , atribut lebih integral ke Serde daripada komentar @nrc membuatnya terdengar. Hampir semua kasus penggunaan tanpa atribut akan dilayani dengan baik oleh rustc-serialize. Jika kita ingin mengeluarkan orang-orang setiap malam, kita dapat melakukannya dengan mencela Serde demi Rustc-serialize.

Saya belum memiliki pendapat tentang langkah yang benar di sini, tetapi saya tahu bahwa jika kita stabil tanpa atribut, saya akan sangat mempertimbangkan penguraian komentar dokumen untuk mengeluarkan atribut. Saya membayangkan banyak orang tidak ingin berurusan dengan skrip build hanya agar mereka dapat menulis:

#[serde(skip_serializing)]

.. dari pada:

/// <!-- serde(skip_serializing) -->

@tokopedia

Menurut pendapat saya, salah satu kasus penggunaan custom derives adalah untuk mengimplementasikan dengan cara yang aman, ciri-ciri yang ditandai sebagai tidak aman, seperti misalnya sifat yang harus menggambarkan dengan tepat tata letak anggota struct tempat penerapannya.

Apa pendapat Anda tentang komentar saya di

Saya ingin menjelaskan satu hal:

Apakah menghapus atribut khusus adalah hal utama yang dilakukan orang-orang dengan turunan khusus?

Saya tahu ada beberapa peretasan untuk melakukan ekspansi foo! dalam konteks suatu jenis (misalnya, @sgrif menyebutkan kasus penggunaan seperti itu dalam diesel, saya pikir @tomaka juga menyebutkannya) - seberapa sentral kasus penggunaan tersebut ?

Alasan saya bertanya adalah ini: Saya melihat manfaat memiliki mekanisme turunan hanya mengembalikan daftar add'l impls. Secara khusus, ini berarti span untuk deklarasi tipe itu sendiri akan benar. Menambahkan API cepat-dan-kotor ke konteks yang memungkinkan Anda memberikan daftar nama atribut ke daftar putih dalam konteks jenis tersebut tampaknya merupakan perbaikan yang cukup mudah untuk atribut, dan kami selalu dapat menghentikannya.

Namun, jika kami ingin mengaktifkan lebih banyak kasus penggunaan (dan terus terang kasus penggunaan tersebut sedikit lebih ... mengejutkan, ketika Anda memikirkan tentang apa yang diharapkan dari turunan), maka ini tidak akan berhasil. Dalam hal ini, saya mungkin akan baik-baik saja dengan menstabilkan apa adanya dan berencana untuk menghentikan API "dari byte mentah" di masa mendatang demi cara yang lebih baik untuk menulis turunan (lagipula, kami tidak benar -

@nikomatsakis Kebutuhan utama bukanlah untuk menghapus melainkan mengabaikan atribut. Saya tidak menyadari kerugian serius jika mengabaikannya. Memberikan daftar putih pada fungsi turunan melalui parameter tambahan ke atribut utama, misalnya, harus mencukupi untuk semua kebutuhan praktis yang benar-benar diturunkan dan bukan peretasan makro prosedural.

Ya, saya terpecah antara dua pendekatan dalam hal ini, yang keduanya memiliki kelemahan yang jelas:

  1. Pendekatan "turunan sederhana" - sesuaikan API untuk hanya menghasilkan item tambahan, membuatnya masih tidak stabil bagi pengguna untuk menggunakan atribut khusus sebagai bagian dari penurunan, dan menutup lubang yang dilewati diesel dan peti lainnya untuk mendapatkan makro prosedural arbitrer.
  2. Pendekatan "keusangan terencana" - menstabilkan API apa adanya, dengan sedikit penyesuaian yang mungkin seperti yang disebutkan Niko tentang penamaan, dengan maksud untuk menghentikannya suatu hari nanti. Ini akan mengharuskan semua penulis turunan kustom suatu hari nanti menulis ulang kode mereka, dan memungkinkan kemungkinan perilaku yang mengejutkan dalam periode sementara.

EDIT: tapi daftar putih @eddyb juga terdengar menjanjikan.

Maksud saya, itu tidak seperti @nrc mengusulkan sesuatu yang jauh berbeda (meskipun IIRC dia berbicara tentang daftar putih di peti pengguna), dan itu menjadi konyol untuk berbicara tentang "menandai atribut sebagai digunakan" ketika semua yang Anda miliki adalah aliran token.

Makro prosedural sewenang-wenang yang saya telah menyalahgunakan sistem makro 1.1 untuk di rust-cpp akan sepenuhnya mungkin dengan pendekatan alternatif yang hanya memberikan kemampuan untuk menambahkan impls dan mengabaikan atribut.

Saya pikir memiliki kemampuan untuk mengabaikan atribut itu penting, tetapi selain itu saya akan baik-baik saja dengan tidak dapat memodifikasi struct di luar titik itu.

Saya tidak yakin bentuk peretasan apa yang digunakan @sgrif di deisel yang tidak akan _possible_ lakukan di dunia di mana kita tidak dapat memodifikasi struct itu sendiri, dan sebagai gantinya hanya dapat menambahkan item tambahan.

Mengizinkan turunan khusus untuk mengambil argumen, seperti yang disarankan oleh @sfackler , mungkin menjadi cara untuk membuat semua orang mendapatkan fungsionalitas yang mereka butuhkan sambil mengizinkan keputusan apa pun tentang atribut khusus ditunda. Ini tidak akan terlihat bagus atau terbaca, tapi itu akan menyelesaikan pekerjaan. misalnya:

#[derive(Debug, Clone, Serialize(field("bar", rename = "baz")))]
pub struct Foo {
  pub bar: String,
}

Formulir ini nantinya tidak akan digunakan lagi karena atribut khusus setelah diambil keputusan tentangnya.

Baik serde dan peti saya memerlukan atribut bidang / varian. Mencoba meniru ini dengan argumen turunan tidak masuk akal, kita membutuhkan atribut.

Apa pun keputusan yang kami buat untuk menstabilkan ini, alangkah baiknya jika pengguna makro turunan khusus tidak perlu mengubah kode mereka ketika / jika kami beralih ke makro 2.0 menurunkan api (jelas penulis makro turunan khusus akan). Tampaknya keputusan yang paling masuk akal adalah memberikan kompiler daftar atribut yang akan dihapus untuk turunan Anda, dan yang terpenting, atribut tersebut hanya dihilangkan setelah makro _all_ derive dijalankan. Serde, diesel, dan my crate semuanya memiliki masalah yang membutuhkan atribut yang sama di beberapa makro turunan.

Dengan perilaku pengupasan, saya tidak memerlukan peti pasca-perluasan yang dibuat @dtolnay untuk menambahkan makro

FWIW satu-satunya alasan untuk menghapus atribut tersebut adalah untuk menjaga HIR lebih ramping - sejauh menyangkut hal lain Anda hanya perlu menandainya sebagai sudah digunakan.

Apakah menghapus atribut khusus adalah hal utama yang dilakukan orang-orang dengan turunan khusus?

Saya tahu ada beberapa peretasan yang harus dilakukan! ekspansi dalam konteks suatu jenis (misalnya, @sgrif menyebutkan kasus penggunaan seperti itu dalam diesel, saya pikir @tomaka juga menyebutkannya) - seberapa sentral kasus penggunaan tersebut?

Saya benar-benar setuju dengan fakta bahwa custom derives tidak dapat mengubah struct.

Saya sering mengeluh tentang kurangnya plugin di Rust yang stabil, jadi ketika saya melihat turunan khusus, saya menggunakannya sebagai kesempatan untuk memiliki plugin. Tapi saya jelas tidak akan membantah bahwa turunan adat harus mendukung sesuatu yang tidak dirancang untuknya.

Sepertinya ada regresi dalam semalam. Mendapatkan kesalahan

Queryable adalah mode turunan

saat menyusun contoh kami.

#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}

@sgrif Itu disebabkan oleh # 37198, yang mengubah turunan khusus untuk menggunakan namespace yang sama dengan makro lain (sehingga bang!() makro, #[attribute] makro, dan #[derive(custom)] makro semuanya berbagi ruang nama yang sama).

Dalam contoh itu, #[macro_use] extern crate diesel; mengimpor makro bang bernama Queryable dan #[macro_use] extern crate diesel_codegen; mengimpor makro turunan kustom yang juga bernama Queryable , yang secara diam-diam menimpa makro bang (selain - #[macro_use] menimpa secara diam-diam tidak ideal, tetapi tidak akan menjadi masalah setelah kami dapat mengimpor makro dari peti eksternal dengan use alih-alih #[macro_use] , segera!).

Saya percaya kesalahan ini disebabkan oleh pemanggilan makro bang Queryable!(); dalam ekspansi turunan kustom, yang menyelesaikan ke kustom yang berasal dari diesel_codegen alih-alih makro bang dari diesel .

@jseyfried Apa alasan menggunakan satu namespace untuk ketiga "jenis" makro? Bagi saya tampaknya tidak masuk akal untuk memperlakukan atribut dan makro bang sebagai menempati namespace yang sama, lebih dari itu akan masuk akal untuk memperlakukan fungsi dan tipe sebagai menempati namespace yang sama (menurunkan makro bahkan lebih sedikit).

@withoutboats Saya pikir itu adalah analogi yang salah, saya lebih suka berpikir bahwa berbagai jenis makro berbagi namespace dengan cara yang sama seperti yang dilakukan oleh berbagai jenis nilai atau jenis (misalnya, fungsi dan konstanta). Ada biaya kompleksitas dalam memiliki namespace dan saya percaya semakin sedikit kita memiliki semakin baik, jadi pertanyaannya adalah mengapa kita membutuhkan namespace yang berbeda? Jelas untuk back compat kita membutuhkan makro untuk berada di namespace yang berbeda dari fungsi dan tipe.

Masalah sebenarnya adalah Anda tidak dapat menggunakan use untuk mengimpor atau mengganti nama makro secara selektif. Jadi pengguna peti tidak memiliki kemampuan untuk mengatasi bentrokan nama makro. Pada saat yang sama, saya tidak berharap hanya mengimpor sebuah kotak ProcMacro untuk bentrok dengan nama makro lokal - saya pikir makro yang diturunkan dimulai dengan derive_ ?

Ada biaya kompleksitas dalam memiliki namespace dan saya percaya semakin sedikit kita memiliki semakin baik, jadi pertanyaannya adalah mengapa kita membutuhkan namespace yang berbeda?

Saya pikir item harus berbagi namespace hanya jika mereka bisa ambigu satu sama lain. consts dan fns berada dalam namespace yang sama karena keduanya digunakan sebagai ident dalam konteks ekspresi. Jenis dan sifat berada dalam namespace yang sama karena sintaks untuk objek sifat.

Ketiga jenis makro ini semuanya dipanggil dengan cara berbeda. Tidak masuk akal bagi saya bahwa mereka harus berbagi namespace, karena tidak akan pernah ada ambiguitas dalam pemanggilan.

Meskipun ada biaya kompleksitas implementasi, saya rasa tidak ada biaya yang signifikan dalam kompleksitas _use_ dengan menspasi secara terpisah. Ketika saya memikirkan tentang biaya kompleksitas fitur bahasa, saya biasanya berpikir tentang kompleksitas penggunaan.


Apakah Anda akan mengatakan bahwa _all_ item harus berada dalam namespace yang sama jika itu bukan perubahan yang mengganggu? Mencoba menjelaskan tentang proses berpikir Anda.

Merefleksikan sedikit lebih banyak, saya akan baik-baik saja dengan mengatakan seharusnya hanya ada 1 namespace di mana semua item berada - saya bahkan lebih suka; Seluruh pola "konstruktor nyata" agak membingungkan IMO - tetapi itu bukanlah keputusan yang dibuat Rust untuk item non-makro, jadi bagi saya sepertinya itu akan lebih konsisten & diharapkan untuk item makro menempati ruang nama yang berbeda.

Masalah sebenarnya adalah Anda tidak dapat menggunakan use untuk mengimpor secara selektif, atau mengganti nama makro.

Anda akan dapat melakukannya dengan makro 2.0. Model di sini pada dasarnya adalah peretasan stop-gap.

Saya pikir makro turunan dimulai dengan derive_?

Itu adalah sistem turunan kustom lama, saya rasa makro 1.1 tidak melakukan ini.

Meskipun ada biaya kompleksitas implementasi, saya rasa tidak ada biaya yang signifikan dalam kompleksitas penggunaan

Ada biaya untuk beberapa definisi penggunaan - ini membuat hidup lebih sulit untuk alat, khususnya (meskipun orang mungkin tidak banyak membantah). Saya juga berpikir bahwa bukan kompleksitas implementasi yang penting (walaupun namespace pasti mempersulit kompiler, saya setuju bahwa ini tidak begitu penting) karena kompleksitas bahasa - pengguna harus beralasan tentang hal ini untuk menggunakan Rust dan memiliki ketukan berdampak pada hal-hal seperti kebersihan dan bayangan, hal-hal yang semakin memperumit.

Apakah Anda akan mengatakan bahwa semua item harus berada dalam namespace yang sama jika itu bukan perubahan yang mengganggu? Mencoba menjelaskan tentang proses berpikir Anda.

Saya tidak akan melangkah sejauh itu - saya pikir ada manfaat untuk nilai dan tipe yang terpisah, tetapi tentu saja bahasa dengan satu namespace akan jauh lebih baik untuk digunakan dalam banyak hal.

tapi itu bukanlah keputusan yang dibuat Rust untuk item non-makro

Counterpoint: modul dan bidang berada di namespace nilai, meskipun tempat mereka dapat digunakan (menurut saya) berbeda dari tempat nilai lain dapat digunakan.

@distroartonline

Masalah sebenarnya adalah Anda tidak dapat menggunakan use untuk mengimpor atau mengganti nama makro secara selektif

Kami akan segera dapat use makro dari extern crate s di belakang gerbang fitur segera (~ 1 minggu), lihat https://github.com/rust-lang/rfcs/pull/1561 dan # 35896. Kami mungkin memutuskan untuk menstabilkan makro use ing dari peti turunan khusus bersama dengan turunan khusus itu sendiri.

Saya pikir makro yang diturunkan dimulai dengan derive_

Ini berlaku untuk turunan adat gaya lama. Dengan makro 1.1 turunan khusus, #[derive(Serialize)] (misalnya) mengharapkan Serialize untuk menyelesaikan ke makro turunan khusus.

@tokopedia

Apa alasan menggunakan satu namespace untuk ketiga "jenis" makro?

  • Diutamakan: makro bang dan makro atribut telah berbagi namespace untuk waktu yang lama, jadi sampai perubahan itu saya pikir turunan khusus juga harus berbagi namespace itu.
  • Kompatibilitas mundur: Jika kita mulai dengan satu ruang nama makro, kita dapat membaginya secara kompatibel. Jika kita mulai dengan beberapa ruang nama makro, kita terjebak dengannya selamanya.
  • Kesederhanaan: Jika setiap makro "jenis" memiliki ruang nama sendiri-sendiri, kami akan memiliki lima ruang nama total. Jadi, setelah https://github.com/rust-lang/rfcs/pull/1561 mendarat, use foo::bar; dapat mengimpor _lima item terpisah_ bernama bar (nilai, tipe, makro bang , dll.), dan tidak akan ada cara sederhana untuk, misalnya, mengekspor ulang makro bang tetapi bukan makro turunan khusus atau hanya mengimpor makro turunan khusus sebagai baz .

Kompatibilitas mundur: Jika kita mulai dengan satu ruang nama makro, kita dapat membaginya secara kompatibel. Jika kita mulai dengan beberapa ruang nama makro, kita terjebak dengannya selamanya.

Ini menarik, terutama karena 1.1 seharusnya menjadi sementara. : +1:

Apakah kode break ini untuk siapa saja yang memiliki makro_rules yang disebut misalnya PartialEq! ?

Tidak, PartialEq tidak ditentukan dalam ruang nama makro hari ini karena ini bukan turunan khusus.
#[derive(Foo)] pertama-tama memeriksa apakah Foo adalah "turunan bawaan" ( PartialEq , Copy , dll.) Sebelum mencari turunan khusus Foo di namespace makro. Bawaannya di-hardcode ke dalam definisi derive .

Karena itu, kami dapat memecahkan kode seperti yang Anda jelaskan jika kami akhirnya memutuskan untuk membuat PartialEq kustom yang diturunkan di awal, bukan bawaan, jadi mungkin ide yang bagus untuk membuktikannya di masa mendatang.

Proposal untuk masalah atribut (dengan asumsi kita mengerjakan model di mana makro yang diturunkan tidak dapat mengubah item yang dideklarasikan, hanya hiasi):

  • di belakang layar, makro turunan kustom menerapkan sifat, saat ini MultiItemModifier . Ini adalah rencana bahwa makro 2.0 akan terus mengimplementasikan suatu sifat dan sifat ini digunakan untuk perluasan mekanisme. Fungsi beranotasi yaitu makro mengimplementasikan sifat ini. Meskipun kami tidak menggunakan registrar plugin, ini pada dasarnya adalah desugaring.
  • kita harus memisahkan sifat CustomDerive khusus untuk macro 1.1 custom derive. Dalam kasus umum, penulis makro tidak pernah melihatnya. Tetapi mereka memiliki opsi untuk mengimplementasikan sifat secara langsung, daripada menggunakan atribut pada suatu fungsi (saya berharap kita akan menggunakan kembali atribut pada impl, mungkin kita perlu membahas mekanisme pendaftaran ini).
  • kita menambahkan fungsi declare_attributes ke CustomDerive yang mengembalikan Vec<String> . Ini memiliki impl default yang mengembalikan vec kosong.
  • Jika penulis makro menerapkan metode ini, atribut apa pun yang dinamai persis seperti salah satu string yang dikembalikan dianggap sebagai milik makro. Atribut semacam itu tidak pernah dilihat seperti makro dan tidak memicu lint atribut khusus. Atribut dibiarkan dalam kode dengan mendapatkan ekspansi, tetapi ditandai sebagai bekas. Atribut semacam itu yang tidak tersentuh oleh ekspansi turunan akan tetap memicu lint atribut yang tidak digunakan (tetapi bukan lint atribut khusus).
  • Kami mungkin di masa mendatang akan memperkenalkan atribut cakupan yang dapat digunakan oleh makro termasuk turunan khusus, ini akan menjadi _dalam tambahan_ hingga declare_attributes dan mekanisme ini tidak akan dihentikan.
  • Alternatif: string yang dikembalikan oleh declare_attributes diperiksa sebagai awalan jalur untuk atribut, misalnya, jika declare_attributes mengembalikan vec!["foo::bar"] , lalu #[foo::bar::baz] dan #[foo::bar::qux] akan diizinkan.

Pikiran? cc @alexrichton @jseyfried @dtolnay @sgrif @erickt @ rust-lang / lang

@nrc akankah mekanisme tersebut mengizinkan untuk menonaktifkan atribut _used_ seperti #[cfg(..)] - entah secara tidak sengaja atau sengaja? Dan apakah itu bisa diubah?

@nrc sayangnya saya tidak bisa melihat seperti apa implementasinya karena kendala tersebut, jadi saya tidak sepenuhnya yakin apakah itu akan berhasil atau tidak.

Meskipun demikian, saya agak skeptis tentang apakah ciri dan objek akan berhasil, karena di mana contoh sifat tersebut dibuat?

@nrc Mengapa ini harus menjadi penting dan tidak bisa hanya menjadi daftar di atribut yang menunjuk fungsi expander derivw? Misalnya #[proc_macro_derive(Serialize, uses_attrs(serde_foo, serde_bar))] atau semacamnya.

@ colin-kiegel Saya membayangkan itu hanya dapat diterapkan dalam lingkup aplikasi turunan, dalam hal ini menonaktifkan atribut lain mungkin adalah perilaku yang diharapkan, meskipun saya pikir kita mungkin menerapkan cfgs sebelum ekspansi makro (ini telah berubah, tetapi saya tidak ingat sebelum vs sesudah).

@alexrichard

Meskipun demikian, saya agak skeptis tentang apakah ciri dan objek akan berhasil, karena di mana contoh sifat tersebut dibuat?

Hmm, poin yang bagus, saya kira kita perlu menangani ini untuk makro 2.0

@eddyb Ini adalah ide yang bagus dan mungkin lebih mudah dilakukan untuk jangka pendek. Alasan saya memilih pendekatan imperatif adalah karena ini adalah mekanisme ekstensibilitas umum dan yang ingin kita pertahankan, sedangkan menambahkan sesuatu ke atribut tidak berskala terlalu baik dan mungkin bukan sesuatu yang ingin kita tahan selamanya. . Di sisi lain, hal ini tentu lebih mudah dilakukan sekarang dan sepertinya bukan hal yang buruk untuk dilakukan, jadi saya pikir ini mungkin taruhan yang lebih baik.

Terjebak - Saya telah keluar dari negara itu dan kemudian sakit. Saya ingin mengunjungi kembali https://github.com/rust-lang/rust/pull/37198 , karena menurut saya tidak masuk akal jika semua jenis makro menempati namespace yang sama. Saya setidaknya mengharapkan makro turunan khusus berada di namespace itu sebagai derive_Foo , atau serupa. Ada kasus penggunaan untuk beberapa jenis makro yang menggunakan nama yang sama bahkan di pustaka standar (mis. #[cfg] dan cfg! )

Saya juga menemukan namespace bersama agak canggung. Dari sudut pandang pengguna akhir, mungkin tampak seolah-olah ada semacam pertukaran, ambiguitas atau sesuatu yang menarik terjadi - yang sebenarnya tidak ada. Saya pikir batasan 'acak' seperti ini bisa membuat pemahaman Rust sebagai bahasa sedikit lebih sulit ..

Namun saya pikir ini mungkin bukan akhir dari dunia. Tampaknya ruang nama yang berbeda masih dapat diperkenalkan di masa depan tanpa banyak merusak.

@sgrif @ colin-kiegel Saya menjelaskan alasan saya untuk satu namespace makro di https://github.com/rust-lang/rust/issues/35900#issuecomment -256247659.

Menurut saya tidak masuk akal jika semua jenis makro menempati namespace yang sama

Agar jelas, makro bang dan makro atribut selalu menempati namespace yang sama; # 37198 baru saja memindahkan turunan kustom ke dalam namespace ini.

Ada kasus penggunaan untuk beberapa jenis makro yang menggunakan nama yang sama bahkan di perpustakaan standar (misalnya #[cfg] dan cfg! )

Atau, cfg dapat dilihat sebagai makro tunggal yang dapat digunakan melalui pemanggilan bang atau pemanggilan atribut (meskipun saat ini tidak memungkinkan bagi pengguna untuk menentukan makro yang dapat dipanggil melalui bang atau atribut, kami mungkin memutuskan untuk mengizinkannya di makro 2.0).

Dari perspektif pengguna akhir, ini mungkin tampak seolah-olah ada semacam pertukaran

Saya pikir ini bisa diperbaiki dengan pesan kesalahan yang jelas ketika dua makro konflik (saya akan bekerja untuk meningkatkan pesan hari ini).

Tampaknya ruang nama yang berbeda masih dapat diperkenalkan di masa depan tanpa banyak merusak

Saya percaya memisahkan namespace makro sepenuhnya kompatibel ke belakang (perbaiki saya jika saya salah). Ini adalah motivasi utama saya untuk mempertahankan satu ruang nama makro hari ini - kami ingin makro 1.1 kompatibel semaksimal mungkin di masa mendatang.

Terakhir, menurut saya konflik makro bang / atribut vs konflik makro turunan khusus akan jarang dalam praktiknya karena makro bang / atribut biasanya menggunakan huruf kecil dan turunan khusus biasanya menggunakan huruf besar. Dengan kata lain, turunan kustom sudah memiliki namespace sendiri saat konvensi penamaan tersebut diikuti.

Sepertinya kerusakan (https://github.com/diesel-rs/diesel/issues/485) disebabkan oleh dukungan, misalnya Queryable! { struct S; } serta #[derive(Queryable)] struct S; . Setelah turunan kustom stabil, tidak perlu lagi mendukung Queryable! { struct S; } jadi ini tidak akan menjadi masalah lagi, bukan?

Sementara itu, saya yakin kami dapat memperbarui diesel sehingga

  • Queryable! masih didukung tanpa #[macro_use] extern crate diesel_codegen; , dan
  • #[derive(Queryable)] , tetapi bukan Queryable! , didukung dengan #[macro_use] extern crate diesel_codegen; .

Jangan ragu untuk menghubungi saya di IRC untuk berdiskusi - saya akan dengan senang hati menulis PR.

@nrc @alexcrichton

Meskipun demikian, saya agak skeptis tentang apakah ciri dan objek akan berhasil, karena di mana contoh sifat tersebut dibuat?

Saya setuju dengan @eddyb bahwa jika yang kita inginkan hanyalah menangani atribut, demi kenyamanan dan kemanfaatan, sebaiknya kita memperluas atribut tersebut.

Tetapi jika kita _did_ menginginkan sistem ekstensi yang lebih fleksibel, maka saya membayangkan kita akan menggunakan ciri-ciri, tetapi ciri-ciri itu akan didefinisikan sebagai kumpulan metode yang tidak mengacu pada Self :

trait CustomDerive {
     fn foo(); // note: no self
     fn bar(); // again, no self
}

Anda kemudian akan menerapkannya pada tipe dummy seperti struct my_annotation_type; (berbagi nama yang sama dengan atribut, saya kira?), Dan kami akan menggunakan resolusi sifat untuk mengekstrak fungsi yang relevan seperti <my_annotation as CustomDerive>::foo (mungkin saat menulis metadata, saya kira). Intinya adalah, kami tidak akan pernah membuat (atau membutuhkan) instance my_annotation , itu hanya mekanisme pengelompokan untuk menyatukan sekelompok fungsi terkait.

Tentu bukan hal yang paling elegan, tapi saya tidak yakin cara yang lebih baik? Saya pikir ketidakselarasan adalah alasan mengapa kami ingin memulai dengan fungsi yang dikaitkan. =)

Mengenai ruang nama, @sgrif cocok dengan contoh #[cfg] dan cfg! . Saya pasti bisa membayangkan seseorang ingin #[derive(SomeTrait)] dan juga memiliki beberapa makro seperti SomeTrait! { ... } yang ... melakukan sesuatu. =) Tapi @jseyfried juga membuat kasus yang bagus dengan kompatibilitas mundur - selama kami tidak mencapai batasan _already_.

Saya cenderung lebih suka ruang nama yang lebih sedikit secara default, sebagian besar karena rasa sakit yang menurut saya memiliki perpecahan namespace nilai / jenis membawa kita sekarang. Karena itu, saya pikir sebagian besar poin nyeri yang diketahui tidak berlaku di sini:

  • pemisahan tipe / nilai adalah masalah dalam resolusi nama karena use foo::bar mungkin atau mungkin tidak mengimpor modul bernama bar , dan karenanya mungkin (atau mungkin tidak) relevan dengan resolusi nama seperti bar::baz ;
  • tipe / nilai split adalah jenis kesulitan untuk generik atas konstanta, meskipun nyeri itu juga karena pemisahan sintaksis antara tipe dan nilai.

Tetapi sebagian besar, karena kita telah menyeberangi jembatan, saya tidak yakin bahwa memiliki "turunan khusus" secara langsung di namespace-nya sendiri membawa tantangan khusus, bukan?

Kita bisa menambahkan prefiks derive_ ke makro yang dibuat, untuk membuatnya berspasi pseudo. Jika kita ingin membuat perubahan itu, sekaranglah waktunya.

@keeperofdakeys Bukankah ini sama dengan penulis turunan ubahsuaian yang memutuskan untuk menamai turunan khusus mereka derive_* ? Terlepas dari itu, menurut saya nama derive_* terlalu jelek untuk pengguna akhir.

@nikomat

Tetapi sebagian besar, karena kita telah menyeberangi jembatan, saya tidak yakin bahwa memiliki "turunan khusus" secara langsung di namespace-nya sendiri membawa tantangan khusus, bukan?

Algoritme resolusi impor dapat menangani banyak ruang nama secara sewenang-wenang, tetapi ia melakukan sejumlah pekerjaan yang berpotensi tidak sepele untuk setiap ruang nama. Secara khusus, untuk setiap impor I dan setiap ruang nama yang tidak digunakan S , ini membuktikan bahwa resolusi I gagal dalam S (cf https: // github. com / rust-lang / rfcs / pull / 1560 # Issuecomment-209119266). Bukti ini sering kali memerlukan DFS dari grafik impor glob (di mana simpul adalah modul dan tepi adalah impor glob) untuk mencari impor tak tentu yang relevan.

Namun, pekerjaan ekstra per namespace ini mungkin tidak membuat perbedaan dalam praktiknya dan dapat dihindari jika diperlukan (cf https://github.com/rust-lang/rfcs/pull/1560#issuecomment-209119266) dengan biaya pembatasan kecil yang hanya akan berlaku untuk ruang nama makro.

Saya menggabungkan namespaces di # 37198 agar sebagian besar opsi kami tetap terbuka dan karena menurut saya itu tidak akan membatasi dalam praktiknya. Jika orang menginginkannya hari ini dan @ rust-lang / lang tidak masalah dengan memiliki beberapa ruang nama makro selamanya, saya tidak keberatan.

@nikomat

Saya pikir ya, strategi Anda akan berhasil, meskipun miring. Perasaan pribadi saya adalah bahwa keanehan tidak menarik bobotnya (misalnya apa yang kita miliki hari ini baik-baik saja bagi saya), tetapi harus dapat diterapkan dengan cara apa pun.

@alexcrichton seperti yang saya pikir saya tulis sebelumnya, saya senang menunggu sampai kita membutuhkan lebih banyak kekuatan (jika pernah) - dan kemudian kita dapat melakukan sesuatu seperti sifat yang saya jelaskan, atau mungkin sesuatu yang lebih baik. Untuk saat ini saya pikir hanya memperluas atribut yang diterapkan ke fn sudah cukup.

Saya menjadi penasaran, dan memulai implementasi dasar dari ide ini (menggunakan atribut pada fungsi proc_macro untuk menunjukkan nama atribut yang akan ditandai sebagai digunakan pada item).

Saya telah menghapus tag FCP karena tampaknya jelas kami belum _quite_ mencapai titik di mana kami siap untuk menstabilkannya. Saya akan berusaha untuk meringkas keadaan percakapan dan, khususnya, untuk menyoroti kesalahan di mana kita masih membutuhkan keputusan tegas dan / atau kontribusi kode:

Pertanyaan 1: Haruskah makro yang diturunkan secara kustom dapat mengubah item yang mereka lewati?

  • Tidak ada yang berpikir mereka harus melakukan itu _ dalam praktiknya_ tentunya
  • Ini mode lain, yang awalnya ditolak dengan semangat YAGNI
  • Turunan yang mengubah kumpulan nama bidang dll tidak tersusun dengan baik, membuat urutan aplikasi terlihat;
    bahayanya dikurangi dengan dua hal:
  • Di sisi lain, jika kita mengatakan bahwa custom derive hanya perlu mengembalikan impls baru

    • informasi rentang lebih baik

    • turunan kustom lebih mudah untuk ditulis

  • _But_ banyak turunan khusus menggunakan atribut khusus untuk memandu perluasannya, dan itu akan menghasilkan peringatan / kesalahan

    • Teknik saat ini adalah menghapus mereka dari AST

  • _Juga: _ kami ingin mengeluarkan hal ini di dunia ASAP, tidak ingin melakukan eksperimen yang lama atau perubahan drastis

Proposal:

  • Pertahankan semuanya sebagaimana adanya, mungkin tidak digunakan lagi nanti

    • Bijaksana, agak disayangkan

  • Perpanjang #[proc_macro] dengan daftar putih atribut, beri tahu rustc untuk menganggapnya "bekas" dan abaikan

Pertanyaan 2: Haruskah turunan kustom berbagi namespace yang sama dengan makro lain?

Argumen untuk:

Argumen melawan:

Proposal:

  • Pisahkan menjadi namespace "turunan khusus"
  • Pertahankan status quo

Hal-hal lain?

Apakah ada pertanyaan terbuka lainnya?

Solusi potensial untuk masalah mutasi:

Alih-alih turunan khusus memiliki tipe TokenStream -> TokenStream , mereka malah akan memiliki tipe
Item -> TokenStream , di mana Item adalah tipe buram yang akan dimulai hanya dengan dua metode:

  • item.tokens() , yang mengembalikan TokenStream yang akan diberikan hari ini, dan
  • item.use_attrs(name) , yang akan menandai semua atribut dengan nama yang diberikan sebagai digunakan.

TokenStream dikembalikan hanya akan menyertakan impl s yang diturunkan.
Kita akhirnya bisa menambahkan API Item dengan fungsi praktis (mis. Melakukan iterasi pada bidang / varian item) atau API turunan tingkat yang lebih tinggi seperti yang ada di syntax_ext::deriving::generic .

Saya akan dengan senang hati menerapkan mengizinkan atribut yang masuk daftar putih di #[proc_macro_derive] (yaitu, proposal kedua untuk pertanyaan 1 di https://github.com/rust-lang/rust/issues/35900#issuecomment-258315395) atau proposal Item -> TokenStream .

Saya pikir itu akan menjadi kesalahan untuk menstabilkan turunan khusus yang dapat mengubah item yang mendasarinya. Informasi rentang yang kita korbankan dengan mengizinkan mutasi sudah menyebabkan masalah - misalnya, kita harus memilih antara memperbaiki # 37563 dan memperbaiki # 36218.

Saya benar-benar tidak melihat daya tarik dari daftar putih wajib, dapatkah ada yang datang dengan kasus penggunaan?

Saya tidak yakin, apakah disengaja / diinginkan kode ini dikompilasi:

#![feature(proc_macro)]

#[proc_macro_derive(Whatever)]
struct Foo {}

@eddyb Saya tidak yakin apakah ada daya tarik yang melekat pada daftar putih wajib - menurut saya manfaatnya
Item -> TokenStream adalah lebih mudah untuk memperluas API Item daripada menambahkan lebih banyak jenis atribut deklaratif. Karena itu, memikirkan hal ini lagi, mungkin tidak ada banyak kasus penggunaan lain yang membutuhkan Item lebih dari TokenStream , jadi TokenStream -> TokenStream dengan daftar putih deklaratif mungkin lebih baik.

Akankah macros 2.0 menggantikan api ini? Jika demikian, ekstensibilitas bukanlah masalah nyata, dan api Item -> TokenStream tampaknya tidak terlalu menarik.

@keeperofdakeys Menurut RFC:

Secara keseluruhan, ini harus dianggap sebagai langkah tambahan menuju "makro 2.0" resmi.

Maksud dari makro 1.1 adalah menjadi sedekat mungkin dengan makro 2.0 dalam semangat dan implementasi, hanya saja tanpa menstabilkan sejumlah besar fitur. Dalam hal ini, maksudnya adalah dengan memberikan makro yang stabil 1.1, kita dapat melapisi fitur-fitur yang kompatibel dengan mundur untuk mendapatkan makro 2.0.

https://github.com/rust-lang/rfcs/pull/1681#issuecomment -233449053 dan https://github.com/rust-lang/rfcs/pull/1681#issuecomment -233708395 dan https: // github. com / rust-lang / rfcs / pull / 1681 # Issuecomment -239558657 tampaknya menunjukkan bahwa hanya penghentian "terbatas" yang diharapkan saat makro 2.0 tiba. Secara khusus: "pola pohon token yang membenarkan string akan menjadi tidak disukai, jika tidak benar-benar dihentikan".

@deddyb

Saya benar-benar tidak melihat daya tarik dari daftar putih wajib, dapatkah ada yang datang dengan kasus penggunaan?

Yah, saya bisa membayangkan kasus di mana nama atribut dibuat secara dinamis dengan cara tertentu (berdasarkan nama jenis atau bidang, mungkin?) - dan karenanya seseorang tidak dapat mendeklarasikannya sebelumnya. Tampaknya semacam buatan, dan tidak seperti praktik yang sangat baik.

Saya menemukan alternatif @jseyfried menarik - bukan karena sifat imperatif menandai atribut sebagai digunakan, melainkan karena Item API mungkin menjadi lebih kaya dari waktu ke waktu. yaitu, kami mungkin mendukung berjalannya kumpulan bidang dengan cara yang sangat terstruktur, memberikan akses ke lebih banyak data (seperti resolusi nama) yang mungkin kami miliki, dll.

Tentu saja, beberapa di antaranya akan datang dari (_eventually_) beberapa bentuk AST API standar juga.

Catatan tentang waktu: Saya benar-benar ingin melihat Macro 1.1 distabilkan di siklus berikutnya. Hal-hal yang kami blokir terasa, pada akhirnya, cukup kecil.

Dalam semangat itu, pendapat saya tentang masalah yang saya jelaskan:

  1. Saya senang dengan deklaratif #[proc_macro] extension _or_ mengubah tipe menjadi Item . Saya juga berpikir bahwa apapun yang kita pilih, kita berpotensi mengadopsi yang lain di masa depan jika itu tampak seperti ide yang bagus. Saya agak ingin pergi dengan mana yang diimplementasikan terlebih dahulu. =)
  2. Mengenai beberapa namespace, saya pikir kita harus menyimpannya di namespace yang sama untuk saat ini. Bagi saya, kombinasi kompatibilitas mundur (yaitu, menjaga opsi kami tetap terbuka) dengan fakta bahwa kapitalisasi sudah membedakan makro "turunan khusus" dari hal-hal lain dalam praktiknya sangat menarik. Membedakan #[cfg] vs cfg! terasa seperti sesuatu yang bisa kita tangani dengan cara lain, atau jika kita mau, kita bisa memperkenalkan ruang nama terpisah _then_. Tampaknya memiliki namespace terpadu tidak merugikan kasus penggunaan turunan khusus secara khusus, yang merupakan satu-satunya hal yang kita bicarakan tentang menstabilkan. Baik?

@nikomatsakis ringkasan Anda terdengar akurat, terima kasih telah menulisnya! Saya sedih karena kami tidak mendapatkan makro 1.1 di Rust 1.14, tetapi saya memahami bahwa ini adalah masalah yang diperdebatkan.

Perasaan pribadi saya tetap untuk menstabilkan semuanya apa adanya di mana turunan khusus harus menghapus atribut dan hanya ada satu namespace.

Saya kurang mengikuti API Item -> TokenStream , apakah aliran token yang dikembalikan masih mencakup item asli atau hanya impls yang ditambahkan? Apakah itu berarti perlu &mut Item ?

@ est31 komentar Anda terdengar seperti bug, dapatkah Anda membuka edisi terpisah untuk itu?

Saya setuju sepenuhnya dengan komentar @nikomatsakis . Saya pikir itu penting yang berasal tidak memiliki kebebasan memerintah untuk mengubah item yang mereka lampirkan, tetapi semua implementasi yang diusulkan tampaknya baik-baik saja bagi saya.

Tampaknya memiliki namespace terpadu tidak merugikan kasus penggunaan turunan khusus secara khusus, yang merupakan satu-satunya hal yang kita bicarakan tentang menstabilkan. Baik?

Masalah muncul karena telah merusak diesel, yang saat ini memiliki makro yang disebut misalnya Queryable! yang dapat Anda bungkus dengan struct untuk mendapatkan Queryable untuknya.

Sebagai seseorang yang tidak menggunakan atau memelihara diesel saat ini (yaitu sebagai seseorang yang tidak terpengaruh oleh apa yang akan saya usulkan: sweat_smile :), menurut saya yang terbaik adalah menyimpan 1 namespace untuk saat ini dan diesel untuk mengubah nama makro tersebut menjadi derive_Queryable! atau sesuatu seperti itu. Mereka tidak akan digunakan lagi jika ini stabil, bukan?

Saya telah membuat PR https://github.com/rust-lang/rust/pull/37614 untuk fitur yang disarankan. Ini menggunakan atribut terpisah #[proc_macro_attributes(..)] , dan Anda tidak perlu lagi mengembalikan item di TokenStream dikembalikan.

Saya mengajukan # 37637 untuk mencari tahu bagaimana makro proc seharusnya memperlakukan $crate .

Hanya untuk memperjelas, apakah kasus penggunaan ini dianggap baik-baik saja atau penyalahgunaan sistem:

Saya menghasilkan struct baru berdasarkan struct dan atribut yang ada di sini dan saya sangat menyukai desain karena memungkinkan saya untuk mengkonsolidasikan berbagai hal menjadi satu struct.

Namun jika sistem mungkin berubah nanti untuk melarang implementasi semacam ini, saya mungkin akan berhenti sekarang daripada berusaha lebih keras (implementasi saat ini hanyalah bukti konsep yang cepat).

@Tokopedia

Perubahan utama yang tidak kompatibel dengan versi sebelumnya adalah Anda tidak dapat lagi memodifikasi item (Anda tidak mengirimkannya kembali ke TokenStream). Saya akan mengatakan bahwa mendapatkan apa pun selain impl tidak dimaksudkan, tetapi tidak ada yang menghentikan Anda dari melakukan ini. Anda hanya perlu berhati-hati tentang bentrokan nama.

Perubahan utama lainnya adalah kemampuan untuk memberikan daftar nama atribut yang seharusnya tidak memicu kesalahan atribut khusus pada item tersebut.

RE: Konflik namespace di Diesel. Saya tidak yakin apakah saya akan menghentikan makro tersebut atau tidak setelah fitur ini stabil, itu akan tergantung pada apakah masih ada keinginan dari beberapa untuk barang gratis ekstensi kompiler. Meragukan. Masih berguna untuk pengujian internal, tetapi mengganti nama tidak masalah untuk itu.

Saya pikir sangat disayangkan kehilangan kemampuan untuk mengubah aliran input. Mampu menghapus item yang sedang dianotasi memungkinkan kami memiliki makro bang dengan sistem ini juga. Saya memahami kekhawatirannya, tetapi sayang kehilangan kemampuan itu karena mereka.

@TheNeikos @sgrif sudut pandang saya adalah bahwa segala sesuatu yang secara serius mengubah masukan bukanlah turunan dan karenanya tidak dimaksudkan untuk dibahas di sini. Saya pikir itu agak berbahaya (kurangnya kebersihan, dll.) Menggunakan custom derives untuk macro proc tujuan umum, jadi saya tidak ingin mendorongnya.

Tidak dapat mengubah item berarti bahwa informasi rentang item disimpan, yang membuat pesan kesalahan pada item lebih jelas (ini juga berarti pesan kesalahan dalam keluaran turunan tidak lagi menunjuk ke rentang item, tetapi rentang atribut turunan). Saya tidak dapat melihat alasan yang baik untuk membiarkan orang menyalahgunakan fitur ini jika kita benar-benar hanya menginginkan makro prosedural yang tepat.

@TheNeikos Saya tidak berpikir kami akan melarang pembuatan struct baru melalui turunan selama Anda tidak memotivasi struct yang Anda peroleh.

Dalam hal apa yang menurut saya idiomatis; Saya pikir tidak masalah untuk menghasilkan tipe baru, tetapi akan jauh lebih baik jika tipe tersebut terkait dengan tipe yang Anda peroleh. Sebagai contoh, bayangkan jika kita bisa mendapatkan IntoIterator untuk sebuah tipe - itu akan melibatkan pembuatan Iterator struct. Secara konseptual, Anda harus mendapatkan perilaku untuk tipe ini, tetapi perilaku itu mungkin bukan "suatu sifat yang tersirat".

@ syrif

Saya pikir sangat disayangkan kehilangan kemampuan untuk mengubah aliran input. Mampu menghapus item yang sedang dianotasi memungkinkan kami memiliki makro bang dengan sistem ini juga. Saya memahami kekhawatirannya, tetapi sayang kehilangan kemampuan itu karena mereka.

Hmm, saya benar-benar simpatik, meskipun jelas (seperti yang dicatat @nrc ) ini bukan untuk apa sistem dirancang, dan itu akan cenderung mengekspos berbagai sudut kasar. Ini mungkin tidak masalah dalam kasus penggunaan Anda. Saya kira pertanyaan kuncinya adalah seberapa cepat kita dapat melanjutkan hal semacam "bang macros 1.1".

PR telah digabungkan, jadi Anda seharusnya dapat melihat perubahan berikut ini di malam berikutnya. Fungsi proc_macro_derive seharusnya tidak lagi mengembalikan item (melakukannya akan memicu kesalahan tentang tipe yang didefinisikan dua kali), dan Anda sekarang dapat memberikan daftar nama atribut ke daftar putih seperti ini #[proc_macro_derive(Derive, attributes(Foo, Bar)] .

cc @dtolnay , @sgrif ^

yang akan segera menyebabkan kerusakan

Ya, saya mengajukan https://github.com/serde-rs/serde/issues/614 untuk melacak pihak kami.

Saya rasa saya telah memperbaiki kerusakan pada Diesel di https://github.com/diesel-rs/diesel/pull/493 , akan tahu pasti setelah nightlies dibangun lagi.

Jadi jika saya membaca utas ini dengan benar, karena makro proc hanya dapat mengeluarkan item tambahan yang ditambahkan ke item awal, itu juga tidak dapat menambahkan lebih banyak penjelasan ke item awal? (Saya melihat penyebutan mengizinkan "anotasi saudara" tapi tidak ada yang lain tentang itu.)

Saya memiliki makro #[derive(newtype)] proc di peti (kecil, tidak diterbitkan) saya yang meluas ke set lain #[derive()] s berdasarkan struktur yang dianotasi. Misalnya #[derive(newtype)] struct Foo(u64) berkembang menjadi #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] struct Foo(u64); sedangkan #[derive(newtype)] struct Foo(::semver::VersionReq) berkembang menjadi #[derive(Clone, Debug, PartialEq)] struct Foo(::semver::VersionReq); . Jadi anggota struct tidak diubah oleh makro ini, tetapi turunan lain ditambahkan ke dalamnya (ini juga tidak mengubah struct).

Ada beberapa lusin struct seperti itu dan masing-masing memiliki sepuluh turunan baru atau lebih setelah makro ini diperluas. Saya suka jika saya menyadari bahwa saya ingin semua u64 newtypes untuk mengimplementasikan satu sifat lagi, saya hanya dapat mengubah set derivasi di satu tempat di kode makro newtype alih-alih di setiap struct.

Saya dulu memiliki makro macro_rules newtype! untuk ini tetapi beralih ke makro proc karena:

  • Ada atau tidaknya komentar dokumen, ada atau tidaknya pub , dll. Ditangani secara gratis tanpa memerlukan jumlah kombinatorial dari lengan pencocokan makro.
  • Bahkan jika saya menulis lengan pencocokan makro kombinatorial, menemukan urutan di mana mereka tidak saling bertentangan itu sulit.

Sayangnya tidak, Anda tidak dapat lagi melakukan ini seperti yang Anda lakukan sebelumnya.

Mengenai kompatibilitas di masa mendatang, fitur ini akan stabil: karena fungsi plugin tidak harus "murni", apakah akan menjadi perubahan yang merusak jika urutan objek yang diberikan ke fungsi diproses berubah di masa mendatang, atau jika rustc mengimplementasikan multi threaded menyusun?

@ est31 Jika kita punya waktu, kita harus mencoba melakukan hal isolasi IPC yang telah disebutkan di sekitar.

Saya secara konsisten melihat ICE di Diesel setelah perubahan terbaru.

../src/librustc_metadata/decoder.rs:490: entri: id tidak ditemukan: DefIndex (1) di peti "diesel_codegen" dengan nomor 28

@sgrif itu akan menjadi masalah # 37788 yang akan diperbaiki oleh # 37793 (semoga saja berakhir besok malam ...).

@ est31 Sudah terlambat pada jam ini untuk menggabungkannya sebelum pembangunan malam dimulai.

https://github.com/rust-lang/rust/issues/37839 adalah masalah penggunaan lib crate yang sendiri menggunakan kotak proc_macro. AFAICT tidak ada tes yang terpengaruh oleh ini karena mereka hanya mengkompilasi modul makro proc, atau modul makro proc dan modul bin yang langsung mereferensikan makro proc.

Edit: Sekarang sudah diperbaiki!

@Arnavion Masalah yang Anda lihat dengan # 37839 telah diperbaiki untuk komplikasi biasa, tetapi tetap rusak saat menggunakan --target , seperti yang dilaporkan di # 37958. Saya telah memberikan kasus uji minimal menggunakan --target yang masih rusak.

@rachmandfc_gabung

Sekarang fitur atribut yang masuk daftar putih telah diterapkan , saya pikir kita harus menstabilkannya! Serde, Diesel, dan pengguna lainnya - sekarang adalah perubahan Anda pada objek jika desain saat ini tidak sesuai untuk Anda. =)

cc @ syrif

Anggota tim @nikomatsakis telah mengusulkan untuk menggabungkan ini. Langkah selanjutnya adalah meninjau oleh tim yang diberi tag lainnya:

  • [x] @alexcrichton
  • [x] @aturon
  • [x] @brson
  • [x] @eddyb
  • [x] @japaric
  • [x] @michaelwoerister
  • [x] @nikomatsakis
  • [x] @nrc
  • [x] @pnkfelix
  • [x] @vadimcn
  • [x] @withoutboats
  • [x] @wycats

Tidak ada masalah yang saat ini terdaftar.

Setelah peninjau tersebut mencapai konsensus, ini akan memasuki periode komentar terakhirnya. Jika Anda melihat masalah besar yang belum pernah diangkat dalam proses ini, silakan angkat bicara!

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

Saya ingin melihat makro bang dieksplorasi dalam waktu dekat. Tidak ada keberatan.

@rfcbot ditinjau

Saat ini, jika TokenStream gagal mengurai LexError kosong dikembalikan . Apakah mungkin untuk mengembalikan pesan kesalahan yang lebih baik di sini, seperti token mana yang gagal diurai? Meskipun pengguna perpustakaan Anda mungkin tidak ingin melihat jenis pesan kesalahan ini.

Ya, itu akan memudahkan penulis makro. Saya harus menulis aliran ke file dan melihatnya di taman bermain untuk menemukan kesalahan.

Saya pikir pengguna akan mendapatkan keuntungan juga, jika hanya untuk mengajukan laporan bug yang lebih baik terhadap makro.

Di https://github.com/rust-lang/rust/pull/38140 , kami memaksa deklarasi turunan kustom menjadi publik. Teorinya adalah bahwa suatu hari kita mungkin menginginkan turunan pribadi dan kemudian kita mungkin ingin membuat perbedaan itu berdasarkan visibilitas fungsi yang menentukan. Bagaimanapun, ini adalah pilihan konservatif. Saya pikir saya akan membiarkannya meresap selama satu atau dua hari agar orang melihatnya sebelum bergabung, karena kami menstabilkan fitur ini.

# 37480 ditutup, yang seharusnya tercermin dalam daftar periksa.

Tetap

@pnkfelix Saya mengambil kebebasan untuk mencentang kotak Anda untuk Anda, karena Anda berada di PTO dan saya yakin sepenuhnya

: bell: Ini sekarang memasuki periode komentar terakhirnya , sesuai ulasan di atas . :lonceng:

psst @nikomatsakis , saya tidak dapat menambahkan label final-comment-period , harap lakukan.

Apakah stabilisasi yang akan datang mengasumsikan bahwa sisa bug yang diketahui dalam daftar periksa di atas akan diperbaiki terlebih dahulu? Atau apakah itu tidak menghalangi stabilisasi?

Ada dua sekarang:

  • # 36935 memiliki komentar yang mengatakan sudah terselesaikan.
  • # 36691 tampaknya tidak menghalangi saya, kami dapat mengizinkan ini untuk berkembang menjadi mod foo; suatu hari nanti di masa mendatang jika kami ingin tanpa merusak apa pun yang saya yakini.

Hei! Ini adalah audit dokumentasi RFC 1636 . Ini adalah fitur utama pertama yang distabilkan sejak RFC 1636 diterima, dan kami harus melakukan pekerjaan yang baik untuk mengikutinya dalam kasus ini.

RFC menyatakan:

Sebelum menstabilkan fitur, fitur tersebut sekarang akan didokumentasikan sebagai berikut:

  • Fitur bahasa:

    • harus didokumentasikan di Rust Reference.

    • harus didokumentasikan dalam The Rust Programming Language.

    • dapat didokumentasikan di Rust by Example.

  • Fitur bahasa dan perubahan pustaka standar harus mencakup:

    • satu baris untuk changelog

    • ringkasan yang lebih panjang untuk pengumuman rilis jangka panjang.

Bagaimana proses yang tepat untuk ini? Haruskah kita menambahkan item daftar periksa ke komentar teratas masalah ini, atau membuat masalah baru untuk dokumentasi pelacakan? Bagi saya sepertinya kita harus memiliki dokumentasi yang memenuhi persyaratan ini di pohon pada rilis 1.15.

cc @ rust-lang / docs

Terima kasih @withoutboats ! Itu yang utama pertama, ya. Saya telah memiliki ini di daftar saya untuk dilihat pagi ini, dan lihatlah, Anda mengalahkan saya untuk itu 😄

Saya membayangkan hal ini selalu bahwa keputusan untuk menstabilkan dan stabilisasi yang sebenarnya adalah terpisah. Artinya, @ rust-lang / lang dapat mengatakan "ini dapat dibuat stabil", tetapi komit untuk menghapus gerbang juga memastikan dokumentasi ada. Di dunia di mana ada buku tidak stabil , ini akan menarik dokumen dari sana ke dokumen stabil; tapi sampai saat itu, keadaan agak canggung.

Mengingat kami baru saja merilis, rencana saya adalah melakukan sesuatu seperti ini:

  1. Tunggu sampai ini meninggalkan FCP
  2. Dapatkan beberapa dokumen. (Saya berencana menulisnya dalam kasus ini)
  3. Buatlah stabilisasi PR.

Mungkin menggabungkan dua dan tiga.

/ cc @ rust-lang / core, karena ini adalah masalah lintas tim.

@steveklabnik kedengarannya bagus untuk saya, dan saya akan baik-baik saja dengan dokumen pendaratan kapan pun (bahkan sebelum penyelesaian FCP). FCP di sini adalah semacam pseudo-selesai karena kami secara tidak sengaja membutuhkan waktu lama untuk masuk.

Ini juga bagus untuk mendapatkan ini dengan cepat sehingga kami dapat memastikan backport yang aman ke cabang beta 1,15!

Jika saya yang pertama mengenai ini, tidak mungkin seburuk itu tetapi mari kita sertakan di bawah bug yang diketahui: menggunakan makro tipe di dalam struct dengan turunan khusus menyebabkan ICE https://github.com/rust-lang/ karat / masalah / 38706

https://github.com/rust-lang/rust/pull/38737 memperbaiki makro tipe ICE: heart :

Saya baru saja membuat # 38749 tentang dokumentasi proc_macro crate.

Saya telah membaca beberapa kali bahwa Macro 1.1 akan distabilkan di 1.15, tetapi 1.15.0-beta.1 dikirim dua minggu lalu dan setidaknya extern crate proc_macro; masih memiliki fitur-gated di dalamnya serta di 4ecc85beb malam 2016 -12-28. Apakah rencana untuk mendukung perubahan stabilisasi?

@SimonSapin ya, itu rencananya, tapi kita harus mewujudkannya!

Ini masih rencananya: p

Jika pengguna menulis #[derive(Foo)] #[foo_def = "definition.json"] struct MyStruct; , penangan makro tidak memiliki cara untuk mengetahui apa itu "direktori saat ini" dan karenanya tidak dapat menemukan definition.json .

Ini memang disengaja dan karenanya tidak akan mudah diperbaiki, dan saya rasa sudah terlambat untuk memperbaikinya.

Anda dapat membuka Span -> FileMap -> nama file -> direktori. Semua yang hilang adalah mengakses informasi melalui proc_macro .

Anda juga perlu memberi tahu compiler untuk menambahkan dependensi ke definition.json sehingga build menjadi kotor jika diubah.

Makro proc dapat menggunakan env::var("CARGO_MANIFEST_DIR") untuk mendapatkan direktori yang berisi Cargo.toml dari peti yang berisi pemanggilan makro. Agaknya foo_def relatif terhadap itu. Lihat https://github.com/dtolnay/syn/issues/70#issuecomment -268895281.

@tomaka itu dapat dilakukan dengan memutasi FileMap, misalnya begini cara makro include_str! melakukannya.

Agaknya foo_def berhubungan dengan itu.

Saya pikir itu tidak terlalu intuitif untuk harus meletakkan jalan relatif ke Cargo.toml.

itu bisa dilakukan dengan memutasi FileMap, misalnya begini cara include_str! makro melakukannya.

Ya saya tahu itu bisa dilakukan, itu tidak bisa dilakukan dengan API makro prosedural saat ini.

Parameternya adalah Item . Dapat diterima untuk menambahkan metode untuk mengambil span dari Item itu, tetapi menambahkan metode ke Item untuk meminta kompiler menambahkan dependensi akan menjadi hack IMO.

Anda bisa pergi ke Span -> FileMap -> nama file -> direktori.

Apakah API ini (khususnya FileMap ) berada di jalur yang akan distabilkan?

Mereka tidak harus seperti itu, sebenarnya saya tidak ingin menstabilkan internal mana pun. Sebagai gantinya, kita dapat menstabilkan API yang mengekstrak informasi tentang Span (misalnya baris, kolom, nama file).

Saya baru saja mendapatkan kesalahan ini di peti milik saya. Apa yang sedang terjadi?

`` error: Cannot use #! [Fitur (proc_macro)] and #! [Fitur (atribut_kustom)] di waktu yang sama
`` ''

@alexreg Jika Anda menggunakan #[derive] , sekarang stabil. Anda tidak perlu #![feature(proc_macro)] .

@exreg
proc_macro_derive s (makro 1.1) sekarang stabil - Anda cukup menghapus #![feature(proc_macro)] .

#[proc_macro_attribute] baru-baru ini mendarat di balik gerbang fitur #![feature(proc_macro)] ; ini tidak sesuai dengan #![feature(custom_attribute)] . #![feature(custom_attribute)] akan digunakan lagi setelah lahan pengganti (https://github.com/rust-lang/rfcs/pull/1755).

@jseyfried Saya pikir kita harus mengubah masalah pelacakan pada proc_macro karena ini ditutup dan tidak berisi informasi yang relevan.

Terima kasih teman-teman. Itu masuk akal.

@abonander Ya, #![feature(proc_macro)] harus mengarah ke # 38356 - Saya harus memverifikasi itu ketika meninjau # 38842. Bisakah Anda mengirimkan PR?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat