Go: embed, cmd/go: tambahkan dukungan untuk file yang disematkan

Dibuat pada 2 Sep 2020  ·  114Komentar  ·  Sumber: golang/go

Pada bulan Juli, @bradfitz dan saya memposting desain draf untuk file yang disematkan . Dokumen tersebut menautkan ke video, kode prototipe, dan diskusi Reddit.

Umpan balik pada desain itu sangat positif.

Saya mengusulkan untuk mengadopsi desain draf file tertanam untuk Go 1.16, dengan satu tambahan, disarankan dalam diskusi, untuk menyederhanakan kasus akses langsung ke byte dalam satu file tertanam.

Selama file mengimpor "embed" ( import _ "embed" jika perlu), itu akan diizinkan untuk menggunakan //go:embed penamaan satu file (tidak ada pola glob atau pencocokan direktori yang diizinkan) untuk menginisialisasi variabel string atau []byte :

//go:embed gopher.png
var gopherPNG []byte

Impor diperlukan untuk menandai file sebagai berisi baris //go:embed dan perlu diproses. Goimports (dan gopls dll) dapat mempelajari aturan ini dan secara otomatis menambahkan impor dalam file apa pun dengan //go:embed sesuai kebutuhan.

Desain file yang disematkan tergantung pada desain draf antarmuka sistem file, yang juga saya usulkan untuk diadopsi di #41190.

Masalah ini _hanya_ tentang mengadopsi desain file yang disematkan , dengan asumsi bahwa desain antarmuka sistem file juga diadopsi. Jika proposal ini diterima sebelum desain antarmuka sistem file, kami hanya akan menunggu desain antarmuka sistem file sebelum mulai melakukan perubahan.

Proposal Proposal-Accepted

Komentar yang paling membantu

Tidak ada perubahan konsensus, jadi diterima.

Semua 114 komentar

Apakah merupakan kesalahan untuk memiliki direktif //go:embed tanpa mengimpor embed ?

@jimmyfrasche Ya, peluru kelima hingga terakhir dalam daftar di https://go.googlesource.com/proposal/+/master/design/draft-embed.md#go_embed -directives.

@rsc Mungkin saya melewatkannya dalam draf, tetapi saya tidak melihat kemampuan untuk menyematkan satu file yang Anda sebutkan dalam komentar Anda.
Juga, apakah Anda dapat menyematkan satu file sebagai string const juga?
Terima kasih untuk proposal yang bagus ini.

@pierrec Tidak ada dalam draft doc ("satu tambahan" adalah teks dalam komentar di atas). String const pada akhirnya dapat memainkan peran dalam memutuskan apakah suatu jenis program diperiksa, yang berarti semua pemeriksa jenis perlu memahami //go:embed'ed consts. Sebaliknya, jika kita tetap berpegang pada vars, tipe checker tidak lebih bijaksana dan dapat dibiarkan sendiri. Sepertinya kita mungkin harus tetap berpegang pada vars.

Apakah ada alasan khusus Anda menginginkan const alih-alih var? Menggunakannya harus hampir sama sejauh efisiensi. (Referensi ke string const akhirnya dikompilasi ke jumlah referensi ke vars tersembunyi.)

Terima kasih untuk penjelasannya. Saya cenderung menyematkan aset statis sebagai string const saat ini, itulah sebabnya saya bertanya. Saya baik-baik saja dengan vars juga!

Menarik, jadi saya bisa melakukan sesuatu seperti:

//go:embed version.txt
var Version string

Dan bahkan berpotensi memiliki komentar //go:generate untuk menghasilkan version.txt. Itu akan memotong kasus penggunaan besar untuk makefiles/ldflags.

Apakah itu kesalahan jika file tidak ditemukan? Jika demikian, di mana secara teknis kesalahan dianggap terjadi? Menghubungkan waktu?

Bisakah kita memastikan bahwa go:embed berjalan setelah go:generate sehingga kita dapat melakukan hal-hal seperti membuat versi dengan mudah, dll?

Bisakah kita memastikan bahwa go:embed berjalan setelah go:generate sehingga kita dapat melakukan hal-hal seperti membuat versi dengan mudah, dll?

Dari pemahaman saya, go:generate akan terjadi dengan go generate sedangkan go:embed akan terjadi dengan go build .

@carlmjohnson Ya, selalu ada kesalahan untuk mengatakan //go:embed foo mana foo tidak ada.
Kesalahan terjadi saat mengkompilasi file sumber yang berisi baris itu.
(Jika Anda menghapus foo setelah mengkompilasi file sumber itu, Anda masih tidak akan sampai ke langkah tautan - perintah go akan melihat bahwa paket tersebut perlu dibangun kembali karena foo telah dihapus.)

Saya pikir proposal ini tidak lengkap tanpa mengatakan sesuatu tentang ETag.
https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fzi0pok/

@tv42 , ya, kami akan membuat ETag berfungsi. Saya tidak yakin apa bentuknya, tetapi kami akan melakukannya.
(Juga ditegaskan di https://github.com/golang/go/issues/35950#issuecomment-685845173.)

Dua Tiga hal yang saya perhatikan dari bekerja dengan mjibson/esc :

  • Karena go:embed tidak perlu membuat file go untuk disematkan sebagai sistem file read-only, itu akan menghilangkan kesulitan mengubah cap waktu pada file go:generate ed yang menentang git porcelain tes pada CI- sangat bagus
  • Satu hal yang tidak saya temukan dalam proposal tetapi perlu adalah kemampuan untuk memuat ulang file yang disematkan secara langsung selama siklus pengembangan. Menggunakan mjibson/esc Saat ini saya dapat melakukannya dengan menginstruksikannya untuk menggunakan sistem file lokal (meskipun tidak akan mengambil file baru) dan mengubah perilaku menggunakan tag build. Aku bertanya-tanya apa yang bisa masuk ke dalam proposal?
  • Perbarui Hal lain yang saya ingat adalah bahwa esc diperlukan untuk dapat menghapus (bagian dari) jalur dasar secara transparan untuk misalnya mengekspor folder aset sebagai root web.

Renungan : Saya kira poin kedua dapat diperbaiki sehubungan dengan proposal io/fs mana saya akan menggunakan sistem file tertanam atau langsung untuk dimasukkan? Terapkan stripping jalur sebagai middleware io/fs ?

@andig Anda sudah dapat menghapus awalan saat menyajikan sistem file melalui HTTP . Saya setuju bahwa pemuatan ulang langsung dapat dilakukan oleh perpustakaan pihak ketiga yang membungkus io/fs .

Satu hal lagi: jika saya mengerti dengan benar, embed akan mempertimbangkan file secara lokal ke paket dan melarang .. . Desain saya saat ini memiliki /assets dan /server/ mana yang terakhir berisi kode server dan hari ini menampung file yang dihasilkan. Dengan proposal ini, embed harus dipindahkan ke folder root karena aset tidak dapat diakses dari server. Ini memberlakukan kendala aksesibilitas yang berbeda dari impor normal. Saya bertanya-tanya apakah ini perlu untuk alasan keamanan atau apakah penyematan modul-lokal harus diizinkan secara umum.

Satu hal lagi: jika saya mengerti dengan benar, embed akan mempertimbangkan file secara lokal ke paket dan melarang .. . Desain saya saat ini memiliki /assets dan /server/ mana yang terakhir berisi kode server dan hari ini menampung file yang dihasilkan. Dengan proposal ini, embed harus dipindahkan ke folder root karena aset tidak dapat diakses dari server. Ini memberlakukan kendala aksesibilitas yang berbeda dari impor normal. Saya bertanya-tanya apakah ini perlu untuk alasan keamanan atau apakah penyematan modul-lokal harus diizinkan secara umum.

Anda dapat membuat file emed.go di direktori aset Anda dan membuat aset tersedia sebagai paketnya sendiri ke seluruh program Anda.

Tujuan eksplisit lainnya adalah untuk menghindari perubahan bahasa. Bagi kami, menyematkan aset statis tampak seperti masalah alat, bukan masalah bahasa.

Sepakat. Menurut pendapat saya, menambahkan gula sintaksis dalam bahasa untuk mendukung perubahan alat ini adalah perubahan bahasa. Saya yakin ini jelas bagi orang lain, tetapi ini secara efektif berkomentar sebagai kode.

Saya sangat merasa bahwa sihir/gula mengurangi kesederhanaan dan keterbacaan bahasa; sangat mudah untuk melewatkan komentar ajaib yang menyematkan file. Meskipun tanggapan terhadap hal ini dapat dengan mudah berupa "oke, kalau begitu jangan gunakan", perubahan ini berarti bahwa peninjau masih harus waspada terhadap orang lain yang menggunakan fitur ini dan harus ingat bahwa komentar seputar deklarasi variabel dapat merusak build atau gagal di waktu kompilasi.

Saya percaya ini akan menambah kebingungan, mengurangi kegunaan bahasa, dan akan menghasilkan binari besar yang buram tanpa manfaat yang jelas (mengenai kekhawatiran terakhir, ini bahkan akan mengarah pada anti-pola membangun kembali binari karena perubahan file biasa ). Jika go mod diperbolehkan untuk --withNonGoCodeAssets , saya yakin ini akan menyelesaikan kebutuhan sebagian besar pengembang yang tidak ingin menulis pipa pembangunan yang lebih kompleks (saya berasumsi distribusi pengguna akhir adalah bagian yang lebih kecil dari masalah bagi pengguna).

@tristanfisher , saya mengerti maksud Anda tentang bahasa vs perubahan perkakas. Ini pasti dekat garis. Alasan saya menganggapnya lebih sebagai perubahan perkakas adalah karena spesifikasi bahasa tidak terpengaruh - apakah suatu program valid tidak berubah, proses pemeriksaan tipe tidak berubah. Semua yang berubah adalah nilai awal variabel itu setelah komentar. Dengan cara ini agak mirip dengan flag -X linker, yang dapat mengatur nilai awal dari var tipe string tingkat atas. Tidak apa-apa bagi kita untuk tidak setuju; Saya hanya ingin memperjelas definisi saya dan menjelaskan perbedaan yang saya buat.

Untuk bloat, saya kira kita harus melihatnya, tapi saya tidak mengantisipasi program menjadi lebih besar dari yang sudah ada. Orang _sudah_ menjalankan alat yang mengubah file arbitrer menjadi kode Go, memeriksanya ke dalam repo mereka, dan membuat kompiler membangunnya. Desain menghilangkan beberapa overhead dari proses ini tetapi tidak memungkinkan sesuatu yang baru. Mungkin orang akan menyalahgunakannya sekarang karena lebih mudah dilakukan, tetapi pada keseimbangan saya tidak berharap itu menjadi masalah besar. (Dan jika beberapa ketergantungan menyematkan sesuatu yang sangat besar sehingga membuat biner Anda membengkak, Anda selalu dapat memilih untuk tidak menggunakan ketergantungan itu.)

Sedangkan untuk membangun kembali karena perubahan file biasa, satu-satunya file yang dapat memicu pembangunan kembali adalah yang ada di modul tingkat atas Anda sendiri, karena dependensi tidak dapat diubah. Jika Anda menemukan pembangunan kembali terjadi lebih sering daripada yang Anda inginkan, satu-satunya penjelasan adalah (1) Anda menyematkan file dan (2) Anda memodifikasi file tersebut. Anda akan memegang kendali penuh untuk melakukan sesuatu tentang kedua penyebab tersebut. (Akan menjadi hal lain sepenuhnya jika pilihan ketergantungan tentang apa yang akan digunakan entah bagaimana memaksa pembangunan kembali tambahan atau biaya lain pada Anda. Tapi itu tidak terjadi di sini.)

@rsc Saya setuju bahwa tidak apa-apa bagi kami untuk tidak setuju dan saya menghargai tanggapan Anda. Perasaan saya adalah bahwa jika itu disertakan secara default dalam perkakas standar dan komentar dapat menyebabkan inisialisasi variabel secara implisit, maka itu adalah perubahan bahasa. Di luar perdebatan itu, saya rasa firasat saya lebih pada arahan sebagai komentar "ajaib" yang perlu dihafal oleh pembaca kode (manusia). Ini dapat diambil pada kesimpulan yang tidak masuk akal untuk menambahkan fitur baru melalui komentar blokir yang ditangani pada waktu pembuatan.

Yang mengatakan, jika ini ditambahkan ke ekosistem, saya akan berterima kasih bahwa mengimpor embed diperlukan -- itu lebih baik daripada tidak sama sekali sebagai "hei, angkat tangan" saat mengaudit kode. Saya pikir go mod mengizinkan non .go akan menyelesaikan sebagian besar kasus penggunaan (saya membayangkan kebanyakan orang akan mengumpulkan file untuk server web) dan juga akan sepenuhnya hidup dalam perkakas.

Saya pikir poin Anda tentang tautan itu bagus. Ini juga membantu menjelaskan perasaan saya tentang ini: jika pengguna akhir (misalnya bukan seseorang yang hanya mengimpor paket) membuat keputusan, tidak ada cara untuk terkejut dengan gumpalan non-kode yang datang untuk perjalanan. Kekhawatiran saya lahir dari meninjau/menyandingkan pekerjaan orang lain dan tanggung jawab "teknisi", itulah sebabnya saya merasa perlu untuk menanggapi.

Saya pikir "kita harus melihat" merangkumnya dengan baik (saya lebih sinis tentang mengasapi/penyalahgunaan).

Saya akan membaca draf desain malam ini, sejauh ini terlihat bagus dari perspektif TinyGo.

Saya hanya ingin mengklarifikasi satu hal:

Di sisi lain, proyek seperti TinyGo dan sistem target U-root dengan lebih banyak RAM daripada disk atau flash. Untuk proyek tersebut, mengompresi aset dan menggunakan dekompresi tambahan saat runtime dapat memberikan penghematan yang signifikan.

Saya tidak tahu tentang U-root, tetapi untuk TinyGo target utamanya adalah mikrokontroler yang biasanya memiliki flash jauh lebih banyak daripada RAM (biasanya faktor 8 atau 16). Pandangan sekilas pada desain draf tampaknya menyarankan idenya adalah untuk menyimpan file dalam memori hanya-baca, yang akan berfungsi dengan baik untuk target ini: file yang disematkan dapat dibaca langsung dari flash. Kemungkinan besar tidak diinginkan untuk target TinyGo untuk mendekompresi file saat runtime.

Proposal io/fs yang bergantung pada ini tampaknya diblokir pada masalah Readdir/FileInfo, yang sedang dibahas di #41188 dan sebelumnya #40352.

Saya telah menyusun API untuk menggantikannya di https://github.com/golang/go/issues/41188#issuecomment -686283661

@andig

Satu hal yang tidak saya temukan dalam proposal tetapi perlu adalah kemampuan untuk memuat ulang file yang disematkan secara langsung selama siklus pengembangan.

embed.Files mengimplementasikan fs.FS, maka yang perlu Anda lakukan hanyalah menggunakan tag build dev vs !dev untuk mengganti variabel antara embed.Files dan FS asli.

Saya mengajukan #41265. Ia menawarkan ReadDir() API baru untuk io/fs.

Saya memiliki masalah yang sama dengan @tristanfisher . Go telah menggunakan komentar ajaib sebagai arahan kompiler untuk waktu yang lama (sejak awal?), Tetapi mereka dimaksudkan untuk kasus sudut dan jarang muncul dalam kode. Mengingat popularitas penyematan konten statis di binari Go, //go:embed kemungkinan akan lebih umum. Mungkin sudah waktunya untuk mempertimbangkan sintaks yang berbeda untuk arahan kompiler?

Sekedar pengingat bahwa mengubah sintaks Go memiliki biaya yang sangat tinggi. Hampir setiap alat Go di luar sana perlu diperbarui dan/atau diperbaiki untuk mendukung sintaks baru, misalnya.

Saya tidak menganggapnya sebagai komentar ajaib. Baris yang dimulai dengan //go: adalah direktif dan dapat didefinisikan seperti itu di spec. Tidak ada banyak perbedaan semantik antara //go:embed , @embed , [[embed]] atau sejumlah variasi sintaks lainnya, kecuali awalan //go: sudah diperlakukan sebagai non -kode oleh alat Go. (editor saya menyoroti baris-baris itu secara berbeda misalnya)

@mvdan Jika proposal ini terjadi, sintaks Go telah berubah. Itu hanya diubah dengan cara yang tidak merusak perkakas yang ada. Mungkin itu tampak bertele-tele.

@iand Saya tidak rewel tentang sintaks khusus untuk arahan kompiler. Saya hanya berpikir bahwa itu perlu diformalkan di beberapa titik dan aturan yang ditentukan.

Saya pikir proposal ini adalah ide yang bagus. Ini memecahkan masalah umum. Kekhawatiran saya adalah bahwa biaya untuk mengadopsinya harus dibuat sedikit lebih eksplisit.

@jonbodner Saya berbagi kekhawatiran Anda tentang komentar ajaib. Tetapi sampai batas tertentu aturan ditentukan oleh #37974.

@networkimprov , ini bukan proposal io/fs. Tolong berhenti berkomentar tentang ReadDir di sini.

@jonbodner

Saya tidak rewel tentang sintaks khusus untuk arahan kompiler. Saya hanya berpikir bahwa itu perlu diformalkan di beberapa titik dan aturan yang ditentukan.

Saya hanya akan menunjukkan bahwa kami membuat keputusan untuk menggunakan //go: untuk menandai arahan toolchain Go ketika
kami menambahkan (penggunaan terbatas) anotasi
Kami menambahkan //go:noescape untuk penulis perakitan pada tahun 2013.
Kami menambahkan //go:generate pada tahun 2014.
Kami kemungkinan akan menambahkan //go:build pada tahun 2020-2021 juga.
Ada yang lain; itu saja highlightnya.
Anda dapat menganggap //go: sebagai arti #pragma dari C jika itu membantu.

Pada titik ini, konvensi sangat mapan.
Kami memilih sintaks itu pada tahun 2012 karena
(1) itu jelas bukan komentar untuk seseorang;
(2) alat yang tidak tahu tentang komentar akan mengabaikannya karena itu adalah komentar; dan
(3) itu digeneralisasi ke alat lain (s/go/yourtool/).

Dan seperti yang dikatakan Ian, #37974 memformalkan sintaks komentar umum yang tepat, untuk apa nilainya.

Berdasarkan pembahasan di atas, sepertinya ini akan diterima .
(Sekali lagi, dengan asumsi tetapi terpisah dari proposal FS.)

Tidak ada perubahan konsensus, jadi diterima.

Saya ingin sekali mendapatkan embed- apakah ini sudah diuji pada master atau apakah ada rencana untuk mengirimkannya sebagai percobaan selama siklus 1,15?

@andig , Go 1.15 sudah keluar. Saya masih berharap ini akan ada di Go 1.16 dan mendarat di cabang pengembangan bulan ini.

@rsc 1.16 tersedia?

@septs , tidak, kami masih mengerjakan Go 1.16. Pembekuan kode adalah 31 Oktober, dengan target tanggal rilis 1 Februari.

rilis 2021Q1 atau 2021Q2 tercepat?

@septs tolong berhenti bertanya tentang rilis Go di utas ini. Lebih dari dua puluh orang mengikutinya dan mendapatkan pemberitahuan. Lihat https://golang.org/wiki/Questions dan https://github.com/golang/go/wiki/Go-Release-Cycle.

Ubah https://golang.org/cl/243941 menyebutkan masalah ini: go/build: recognize and report //go:embed lines

Ubah https://golang.org/cl/243940 menyebutkan masalah ini: go/build: refactor per-file info & reader

Ubah https://golang.org/cl/243942 menyebutkan masalah ini: embed: implement Files

Ubah https://golang.org/cl/243944 menyebutkan masalah ini: cmd/compile: add //go:embed support

Ubah https://golang.org/cl/243945 menyebutkan masalah ini: cmd/go: add //go:embed support

Satu detail yang muncul dalam tinjauan implementasi adalah bahwa "File" sebagai kata benda tunggal cukup canggung ("A File memegang ...").

Pilihan embed.Files untuk nama sudah ada sebelum proposal io/fs dan juga dukungan untuk string dan []byte.
Mengingat kedua perkembangan tersebut, salah satu cara yang tampaknya masuk akal untuk menyelesaikan masalah "A Files hold" adalah dengan menyebutnya FS alih-alih Files.

Kemudian tiga cara untuk menyematkan & mencetak data adalah:

import "embed"

//go:embed hello.txt
var s string
print(s)

//go:embed hello.txt
var b []byte
print(string(b))

//go:embed hello.txt
var f embed.FS
data, _ := f.ReadFile("hello.txt")
print(string(data))

Itu tampak lebih jelas tentang apa yang Anda dapatkan: string, []byte, atau FS.
Artinya, sebagian besar fungsi embed.F* berasal dari fs.FS, dan menyebutnya FS membuatnya lebih jelas daripada menyebutnya File.

Saya telah membuat perubahan itu dalam draf terbaru saya dari embed paket implementasi CL, tetapi saya ingin kembali ke sini dan melihat apakah ada keberatan dengan perubahan nama.

(Perubahan yang lebih radikal adalah melakukan var f fs.FS alih-alih var f embed.FS , tetapi itu akan menghalangi memiliki metode apa pun pada f selain Open. Misalnya, di atas, memiliki ReadFile nyaman dan tidak mungkin. Dan secara umum kita telah belajar bahwa menggunakan tipe konkret untuk sesuatu yang mungkin ingin menambahkan metode nanti adalah pemeriksaan masa depan yang baik dibandingkan dengan menggunakan tipe antarmuka secara langsung.)

Saya pikir mengganti nama adalah perubahan yang baik.

Sehubungan dengan perubahan yang lebih radikal:

  • Jika kita akan menggunakan fs.FS , apakah kita akan membutuhkan paket embed lagi? Saya kira nilai dinamis masih harus memiliki beberapa tipe, yang tinggal di beberapa paket? Saya menemukan ide tidak harus menambahkan paket plus.
  • Saya tidak menemukan "kita tidak dapat menambahkan metode" super meyakinkan, karena IMO f.ReadFile(…) tidak signifikan kurang lebih nyaman dari fs.ReadFile(f, …) .
  • Saya setuju bahwa tipe beton lebih baik secara umum, jadi itu nilai tambah untuk mempertahankannya embed.FS
  • Pertanyaan lain: Apakah embed.FS menggunakan penerima pointer, atau penerima nilai? IMO harus melewati &f canggung, menggunakan penerima nilai sedikit tidak terduga. Kami mungkin juga mengizinkan var f *embed.FS . Jika variabel memiliki tipe antarmuka, pertanyaan ini akan hilang.

Secara keseluruhan, saya masih setuju menggunakan beton embed.FS lebih baik - jika tidak ada yang lain, maka untuk tujuan dokumentasi.

Sekarang setelah Anda menyebutkannya, saya rasa saya tidak memahaminya dengan jelas: kita dapat menyematkan direktori, bukan?

Ya sebagai embed.FS yang mengimplementasikan fs.FS.

@Merovius , embed.FS menggunakan penerima nilai. embed.FS adalah struct satu kata yang berisi satu pointer, jadi tidak ada overhead yang sebenarnya untuk melakukannya, tetapi itu berarti Anda dapat menetapkannya dan menggunakannya tanpa mengkhawatirkan *s dan \&s di mana-mana.

@chabad360 , ya, Anda dapat menyematkan direktori.

Bagaimana dengan symlink?

@burik666 , lihat https://golang.org/s/draft-embed-design untuk detailnya, tetapi tidak, Anda tidak dapat menyematkan symlink.

apakah mungkin untuk menyematkan dan menggunakan pustaka C dinamis? jika demikian bagaimana kita menggunakan jalur embed di #cgo header seperti: #cgo LDFLAGS: -L./lib -lmylib -Wl,-rpath=./lib ?

@benitogf Saya berasumsi satu-satunya cara nyata untuk melakukan ini adalah dengan menulisnya ke disk dan menggunakan dlopen . Saya tidak dapat membayangkan bagaimana Anda dapat memberi tahu pemuat dinamis cara menemukan file yang disematkan. Juga, jika Anda ingin menggabungkan kode C, tautan statis tampaknya lebih tepat, bukan?

@benitogf Embedding memungkinkan Anda meletakkan file dari disk ke dalam []byte dalam program Anda dengan nyaman, tidak lebih.
Jika Anda memiliki cara untuk menggunakan pustaka C dinamis yang sudah ada di program Anda dalam bentuk []byte, maka embedding akan membantu Anda mendapatkan file disk di sana. Jika tidak, tidak.

hubungan statis tampaknya lebih tepat, bukan?

@Merovius setuju, tetapi saya memiliki beberapa kasus penggunaan yang bekerja dengan vendor yang hanya akan menyediakan lib dinamis

Jika Anda memiliki cara untuk menggunakan pustaka C dinamis yang sudah ada di program Anda dalam bentuk []byte
satu-satunya cara nyata untuk melakukan ini adalah dengan menulisnya ke disk dan menggunakan dlopen

menulis perpustakaan yang disematkan dari []byte ke sistem file dan menggunakan dlopen tampaknya ok, meskipun memiliki file yang disematkan secara opsional "dibuang" ke sistem file saat dibangun/dijalankan sehingga header #cgo dapat mengaksesnya akan berguna, tidak hanya untuk cgo imho

Mencoba ini sekarang; satu kutil dengan direktif go:embed adalah jika saya menyematkan build/* , nama file masih memiliki awalan build/ . Jika saya ingin menyajikan direktori itu melalui http.FS , tidak ada cara mudah untuk _add_ awalan yang diperlukan untuk mengaksesnya jika diperlukan (tanpa menulis pembungkus, yang kemudian menimbulkan masalah karena perlu mencantumkan setiap metode potensial bahwa FS mungkin memiliki ...).

misalnya:

//go:embed build/*
var buildDir embed.FS

// Serve some SPA build dir as the app; oops, needs to be build/index.html
http.Handle("/", http.FileServer(http.FS(buildDir)))

// or

//go:embed static/*
var staticDir embed.FS

// Oops; needs to have a static prefix.
http.Handle("/static/*, http.StripPrefix("/static", http.FileServer(http.FS(staticDir))))

// Could be this, but only because the prefix happens to match:
http.Handle("/static/*, http.FileServer(http.FS(staticDir)))

Saya tahu tujuannya adalah agar seseorang dapat menulis go:embed foo/* bar/* baz.ext dan mendapatkan semua file itu, tetapi saya pikir akan sangat umum untuk hanya menyematkan direktori dan menyajikannya sebagai aset statis melalui paket http. Saya berharap ini menjadi gotcha karena orang beralih dari hal-hal seperti http.Dir("static") atau pkger.Dir("/internal/web/static") mana awalan sudah ditangani, ke embed.FS .

Saya tidak begitu yakin bagaimana cara mengajukan ini, karena ini semacam interaksi dengan embed , io/fs , dan net/http .

@zikaeroh Menulis http.Handler juga akan berfungsi di sana kan? Itu hanya satu metode dan bahkan ada http.HandlerFunc . Mungkin perpustakaan standar bahkan dapat menyediakannya untuk dicerminkan http.StripPrefix (sesuatu seperti http.AddPrefix atau http.ReplacePrefix ).

Berpotensi, meskipun terasa agak aneh untuk memodifikasi permintaan HTTP untuk mengatasi implementasi FS (sebagai lawan dari "beri saya FS yang merupakan subdir dari FS lain", yang tidak sederhana dengan metode opsional). Itu tidak akan menjadi hal yang paling efisien, menghapus kemudian menambahkan awalan lain lagi (diberikan salinan http.Request ), tapi saya akan mencobanya nanti. Setidaknya tidak _berbeda_ dari skema saat ini di mana Anda harus bekerja dengan permintaan, saya kira.

Saya memiliki beberapa tempat lain yang saya gunakan data statis bukan melalui paket http yang harus saya buat dengan perbaikan serupa.

Jika saya harus melihat bagaimana penerapannya di mana saya bisa melihatnya. Sebuah cabang di mana itu sedang dilaksanakan?

Disarankan sebelumnya untuk menyematkan file di tempat, yaitu lakukan itu di direktori build dan kemudian impor. Itu akan menghapus awalan build. Kemudian gunakan handler untuk menambahkan awalan yang diperlukan. Saya tidak yakin bagaimana mengecualikan file go melakukan penyematan dari penyematan itu sendiri. Lihat https://github.com/golang/go/issues/41191#issuecomment -686621090

Disarankan sebelumnya untuk menyematkan file di tempat, yaitu lakukan itu di direktori build dan kemudian impor. Itu akan menghapus awalan build. Kemudian gunakan handler untuk menambahkan awalan yang diperlukan. Saya tidak yakin bagaimana mengecualikan file go melakukan penyematan dari penyematan itu sendiri. Lihat #41191 (komentar)

Sayangnya, itu tidak baik untuk direktori yang dihasilkan oleh perkakas lain, misalnya keluaran katakanlah webpack build atau CRA (di mana mereka sering dibersihkan sebelumnya, dan tidak diperiksa). Saya lebih suka meretas nama file.

Disarankan sebelumnya untuk menyematkan file di tempat, yaitu lakukan itu di direktori build dan kemudian impor. Itu akan menghapus awalan build. Kemudian gunakan handler untuk menambahkan awalan yang diperlukan. Saya tidak yakin bagaimana mengecualikan file go melakukan penyematan dari penyematan itu sendiri. Lihat #41191 (komentar)

Sayangnya, itu tidak baik untuk direktori yang dihasilkan oleh perkakas lain, misalnya keluaran katakanlah webpack build atau CRA (di mana mereka sering dibersihkan sebelumnya, dan tidak diperiksa). Saya lebih suka meretas nama file.

Jika Anda menggunakan sistem plugin yang sangat besar seperti webpack, mudah saja untuk menginstal plugin webpack lain untuk menghasilkan embed.go di sepanjang aset itu sendiri. Jika Anda menggunakan sesuatu yang lebih sederhana dengan skrip makefile atau shell, juga mudah untuk menghasilkan file .go dari sana.

@zikaeroh

sebagai lawan dari "beri saya FS yang merupakan subdir dari FS lain" yang digeneralisasi, yang tidak sederhana dengan metode opsional

Ini seharusnya sederhana. Menangani masalah pembungkusan adalah bagian dari proses desain. Secara khusus, pembungkus seharusnya mengimplementasikan semua metode opsional, hanya dengan memanggil fungsi pembantu yang sesuai di fs . Jika itu tidak berhasil, itu mengkhawatirkan dan akan sangat bagus untuk mendapatkan beberapa detail.

Juga, IMO, implementasi pembungkus seperti itu (yang menghapus awalan) pada akhirnya harus disediakan oleh io/fs (analog dengan io.LimitWriter , dll). Saya akan menganggap satu-satunya alasan itu belum terjadi adalah waktu.

@andig Masalah dengan melakukan itu adalah bahwa Go-file yang berisi arahan-embed dan variabel kemudian juga terlihat dari FS (dan akan dilayani oleh HTTP atau mungkin diekspos dengan cara lain).

Masalah dengan melakukan itu adalah bahwa Go-file yang berisi arahan-embed dan variabel kemudian juga terlihat dari FS (dan akan dilayani oleh HTTP atau mungkin diekspos dengan cara lain).

Salah satu cara untuk memperbaikinya adalah dengan menambahkan kemampuan untuk mengecualikan file/folder tertentu dari penyematan ( @rsc ?)

Salah satu cara untuk memperbaikinya adalah dengan menambahkan kemampuan untuk mengecualikan file/folder tertentu dari penyematan ( @rsc ?)

Proposal telah diterima lebih dari sebulan yang lalu dan sudah dilaksanakan; Saya tidak berpikir perubahan desain besar seperti dapat mengecualikan jalur masuk akal pada saat ini. Jika Anda memiliki masalah dengan desain yang diterapkan yang tidak dapat Anda atasi, saya sarankan untuk mengajukan laporan bug terpisah dengan detail, yang kemudian dapat dilacak sebelum rilis final 1.16.

@Merovius

Ini seharusnya sederhana. Menangani masalah pembungkusan adalah bagian dari proses desain. Secara khusus, pembungkus seharusnya mengimplementasikan semua metode opsional, dengan hanya memanggil fungsi pembantu yang sesuai di fs. Jika itu tidak berhasil, itu mengkhawatirkan dan akan sangat bagus untuk mendapatkan beberapa detail.

Bagaimana pengupasan awalan bekerja untuk Glob ?

@icholy saya menganggap sesuatu seperti

func (f *stripFS) Glob(pattern string) (matches []string, err error) {
    matches, err = fs.Glob(f.wrapped, path.Join(f.prefix, pattern))
    for i, m := range matches {
        matches[i] = strings.TrimPrefix(m, f.prefix+"/")
    }
    return matches, err
}

Mungkin dengan beberapa perawatan tambahan.

Bermain dengan ini di gotip, saya perhatikan bahwa itu akan menyertakan file .DS_Store. Ini cukup banyak yang tak terhindarkan, saya kira, tapi saya khawatir memasukkan file titik akan menyebabkan penyertaan file yang tidak disengaja. Mungkin dokumen harus memiliki peringatan keras tentang ini?

Shell saya tidak menyertakan file titik di * , jadi jika saya ingin memasukkannya, saya harus menggunakan * .* . Ini mungkin cara (mungkin sama mengejutkannya) untuk memberikan tingkat kontrol.

Saya tidak yakin harus berpikir apa - IMO, file titik seharusnya tidak benar-benar diperlakukan berbeda oleh polanya, tetapi OTOH contoh .DS_Store sepertinya sesuatu yang benar-benar harus ditangani.

Mengapa tidak melakukan git clean -dffx && go build ? Jika file DS_Store ada di git, maka file tersebut akan disertakan dalam file build. Jika tidak, mereka tidak akan. Ini juga akan bekerja dengan baik dengan gitignore.

Bagaimanapun, Anda harus membangun dengan checkout VCS yang bersih. Jika Anda menambahkan file sementara acak, mereka mungkin membuatnya menjadi versi final dan Anda tidak mungkin mengetahui file apa yang akan dibuat orang. Kami mungkin ingin mendokumentasikan ini.

@mvdan Saya setuju pada prinsipnya, tetapi dalam praktiknya, saya khawatir banyak orang akan terbakar oleh bangunan kotor jika mereka tidak diperingatkan. Saya tidak ingin melihat kebocoran rahasia seseorang tidak menyadari file .env mereka disematkan secara tidak sengaja. Saya telah melihat banyak contoh kesalahan yang setara dengan hosting PHP, meskipun itu dapat dengan mudah dicegah dengan memberi tahu Apache untuk mengecualikan file titik.


Re: menyematkan http.FileServers

Jika Anda menggunakan sistem plugin yang sangat besar seperti webpack, mudah saja untuk menginstal plugin webpack lain untuk menghasilkan embed.go di sepanjang aset itu sendiri.

Itu benar, tapi itu sangat fiddly. Inti dari embed.FS adalah untuk mengurangi kebutuhan akan solusi Makefile yang gila. Saya pikir solusi sederhananya adalah memiliki fs.WithPrefix(string) fs.FS yang mengunci FS ke dalam subdirektori. Saya pikir ada beberapa diskusi tentang ini dalam proposal, tetapi saya tidak dapat menemukannya sekarang. Mungkin itu hanya diperdebatkan di Reddit atau sesuatu.

Salah satu cara untuk memperbaikinya adalah dengan menambahkan kemampuan untuk mengecualikan file/folder tertentu dari penyematan ( @rsc ?)

Itu bisa seperti

//go:embed static
//go:embed-exclude .*
var staticFiles embed.FS

Arahan embed-exclude hanya bisa melakukan filter glob pada file yang diterima dan menghapus semua kecocokan…

Jika kami ingin menghindari menambahkan lebih banyak ke proposal ini, itu juga bisa menjadi aturan lint yang memeriksa sistem file yang disematkan untuk kemungkinan dotfile yang tidak terduga dan memperingatkan Anda sehingga Anda dapat memperbaiki build Anda untuk menghapusnya.

Atau kecualikan file titik secara default kecuali disebutkan secara khusus dengan menambahkan .* atau yang serupa.

Itu masih tidak akan mengekspos file assets.go. Adapun proposal yang sudah dilaksanakan, harap dicatat bahwa pertanyaan tentang pembuatan aset telah diajukan selama tahap diskusi. Mungkin tidak berbahaya untuk memiliki (jika tidak kosong, kecuali untuk arahan embed) assets.go terbuka tetapi akan lebih bersih untuk tidak memilikinya. Seperti biasa ada segala macam solusi yang bisa diterapkan.

Mungkin tidak berbahaya untuk memiliki (jika tidak kosong, kecuali untuk arahan embed) assets.go terbuka tetapi akan lebih bersih untuk tidak memilikinya.

Saya setuju bahwa ini sangat tidak mungkin menjadi masalah, tetapi saya tidak suka melihat kode sumber tertutup secara tidak sengaja bocor karena kesalahan konfigurasi jika kita dapat membuatnya mudah untuk mengonfigurasi berbagai hal dengan benar.

Saya tidak ingin melihat kebocoran rahasia seseorang tidak menyadari file .env mereka disematkan secara tidak sengaja.

Jika seseorang menggunakan //go:embed static/* dan ada static/.env atau static/.super-secret , bukankah Anda akan mengatakan bahwa pengguna benar-benar bermaksud memasukkan file-file itu? Kalau tidak, mengapa mereka berada di direktori statis?

Saya mengerti bahwa ini sesuai dengan apa yang diharapkan pengguna dan apa arti * dalam sebagian besar konteks, tetapi saya pribadi berpikir bahwa https://golang.org/pkg/path/filepath/#Glob semantik adalah satu-satunya kebaikan kami pilihan. Ini adalah yang paling sederhana dan akan digunakan oleh sebagian besar pengguna Go dalam konteks mengembangkan Go.

Saya pikir peringatan terhadap bahaya menyematkan * adalah ide yang bagus dalam hal apa pun, karena dalam sejumlah besar kasus seseorang dapat mengurangi kemungkinan kesalahan dengan menggunakan gumpalan yang lebih spesifik seperti *.png .

Juga, saya masih mendorong Anda untuk mengajukan masalah terpisah yang dapat dilacak terhadap rilis 1.16, yang ditulis dari sudut pandang laporan bug. Proposal ini diterima dan dilaksanakan, jadi saya membayangkan itu akan segera ditutup. Misalnya, Anda dapat menyusun laporan bug seperti: dukungan file yang disematkan dengan mudah mengarah ke menyertakan file yang tidak diinginkan (dan memberikan beberapa contoh).

Jika seseorang menggunakan //go:embed static/* dan ada static/.env atau static/.super-rahasia, bukankah Anda akan mengatakan bahwa pengguna benar-benar bermaksud memasukkan file-file itu? Kalau tidak, mengapa mereka berada di direktori statis?

Ada sejumlah besar kasus sudut, misalnya Anda membuka sesi edit dengan vim, tetapi tidak menutupnya, dan itu membuat file .*.swp dengan beberapa konten yang tidak ingin dilihat siapa pun.

Memindahkan diskusi ke #42321.

Ini akan sangat dihargai oleh tim Prisma untuk klien Database Go kami, karena kami menggunakan mesin kueri saat runtime yang ditulis dalam rust dan entah bagaimana harus ada di dalam go binary bawaan.

Cara yang kami lakukan saat ini adalah mengemas biner ke dalam file .go saat go:generate time, tetapi ukuran file jauh lebih tinggi daripada file biner .gz.

Penyematan asli akan membuat ini jauh lebih baik sehingga kami dapat langsung menyematkan file .gz ke dalam biner go terakhir.

Jika seseorang menggunakan //go:embed static/* dan ada static/.env atau static/.super-secret , bukankah Anda akan mengatakan bahwa pengguna benar-benar bermaksud memasukkan file-file itu?

Tidak, saya tidak mau.

$ mkdir z
$ touch z/.secret z/intended
$ ls z/*
z/intended
$ ls z
intended

Lihat komentar saya nanti di https://github.com/golang/go/issues/42328#issuecomment -720169922.

Saya suka ide untuk membuat file/template statis dapat disematkan yang pasti dapat sangat meningkatkan produktivitas go dev.

Tapi haruskah kita berinovasi tag lain (misalnya @ atau apa pun) selain menggunakan kembali ini // , yang seharusnya menjadi komentar?

Sampai sekarang, // telah digunakan secara berlebihan, saya kira, pikirkan ini:

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:webhook:verbs=create;update,path=/validate-batch-tutorial-kubebuilder-io-v1-cronjob,mutating=false,failurePolicy=fail,groups=batch.tutorial.kubebuilder.io,resources=cronjobs,versions=v1,name=vcronjob.kb.io
// +optional
...

Tapi haruskah kita berinovasi tag lain (misalnya @ atau apa pun) selain menggunakan kembali ini // , yang seharusnya menjadi komentar?

Itu adalah diskusi terpisah dan meskipun akan lebih baik jika arahan tidak menjadi komentar, banyak kode compat go1 sudah bergantung pada itu, jadi kemungkinan besar tidak akan berubah.

Tapi haruskah kita berinovasi tag lain (misalnya @ atau apa pun) selain menggunakan kembali ini // , yang seharusnya menjadi komentar?

Saya mencoba menemukannya dan gagal, tetapi saya ingat ada keputusan untuk menstandardisasi //go:<word> untuk arahan semacam ini.
Sepertinya bukan ide yang baik untuk mengubah sintaks, mengingat keputusan untuk secara tegas menyatu pada mereka.
(Juga, tentu saja, itu adalah komentar khusus sehingga kompiler mengabaikannya - arahan ini khusus untuk alat go, jadi mereka tidak boleh bocor ke bahasa yang tepat)

Saya melihat penyebutan pada masalah io/fs yang embed.FS mendukung ETag: https://github.com/golang/go/issues/41190#issuecomment -737702433

Saya mencoba menjalankan tes untuk itu tetapi saya tidak melihat set header respons ETag . Mungkin saya salah memahami penggunaannya. Haruskah saya berharap untuk melihatnya di sini? https://play.golang.org/p/Wq5xU5blLUe

Saya rasa tidak. http.ServeContent (digunakan oleh http.FileServer ) memeriksa header ETag, tetapi tidak menyetelnya, AIUI.

Dalam komentar di atas Russ mengatakan bahwa ETag akan dibuat untuk bekerja . Kesulitannya adalah bagaimana embed.FS berkomunikasi dengan http.FileServer informasi yang diperlukan untuk mengatur ETag atau header caching lainnya. Mungkin harus ada masalah pelacakan terpisah untuk ini.

Secara pribadi, saya berpendapat bahwa embed.FS harus menggunakan waktu komit terakhir dari modul yang relevan sebagai ModTime . Ini kira-kira sesuai dengan debug.BuildInfo , sehingga tidak akan memengaruhi reproduktifitas. Meskipun saya tidak yakin bagaimana itu diatur untuk komit yang tidak sesuai dengan rilis yang ditandai dan itu masih akan meninggalkan pertanyaan tentang apa yang harus disetel untuk membangun dari worktrees kotor.

Tapi, saya percaya @rsc memiliki solusi yang baik dalam pikiran :)

Saya tidak yakin saya mengerti komentar tentang "waktu komit"; jika sumber modul adalah file zip, tidak ada "komit". Saya tidak melihat waktu yang disebutkan dalam debug.BuildInfo atau debug.Module .

Lebih penting lagi, saya berpendapat bahwa mekanisme berbasis stempel waktu apa pun benar-benar lebih rendah daripada etag yang tepat (berbasis konten).

@tv42 Setiap versi modul adalah a) versi semantik yang diturunkan dari tag (yang menunjuk pada komit) atau b) versi semu yang berisi hash komit. Menurut saya? Setidaknya di git. Saya mungkin salah paham tentang sesuatu.

Lebih penting lagi, saya berpendapat bahwa mekanisme berbasis stempel waktu apa pun benar-benar lebih rendah daripada etag yang tepat (berbasis konten).

Saya tidak yakin. Itu membutuhkan beberapa saluran samping untuk mengomunikasikan hash, atau server perlu menghitung hash file berdasarkan permintaan (yang tampaknya cukup mahal). Lagi pula, net/http tidak tahu, apriori, jika konten fs.FS dapat berubah. Hasil akhir dari ETag berbasis hash mungkin membenarkan biaya penambahan saluran samping seperti itu (seperti antarmuka opsional), tetapi tampaknya tidak sepenuhnya lebih baik.

Juga, saya berpendapat bahwa mendukung setidaknya juga pendekatan berbasis waktu berarti Anda dapat bekerja dengan lebih banyak klien. Saya tidak memiliki data untuk mendukung itu, meskipun (yaitu saya tidak tahu apakah dan berapa banyak klien di luar sana yang mungkin mendukung If-Modified-Since tetapi tidak ETag dan sebaliknya).

Tapi sungguh, saya tidak terlalu peduli pendekatan mana yang dipilih. Saya hanya ingin menyebutkan opsi untuk menggunakan waktu versi modul ditandai.

Modul @Merovius Go ditentukan dalam bentuk file zip. Fakta bahwa Git adalah sumber umum yang digunakan untuk membangun itu tidak mengubah spesifikasi. Ritsleting memiliki stempel waktu nol:

$ unzip -v ~/go/pkg/mod/cache/download/golang.org/x/crypto/@v/v0.0.0-20200510223506-06a226fb4e37.zip|head
Archive:  /home/tv/go/pkg/mod/cache/download/golang.org/x/crypto/@v/v0.0.0-20200510223506-06a226fb4e37.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
     345  Defl:N      233  33% 1980-00-00 00:00 237856c8  golang.org/x/[email protected]/.gitattributes

Tampaknya ada stempel waktu dalam file *.info menyertainya, tetapi saya tidak tahu apakah itu dapat diandalkan atau tersedia untuk perkakas.

$ cat ~/go/pkg/mod/cache/download/golang.org/x/crypto/@v/v0.0.0-20200510223506-06a226fb4e37.info; echo
{"Version":"v0.0.0-20200510223506-06a226fb4e37","Time":"2020-05-10T22:35:06Z"}

Meski begitu, stempel waktu apa yang harus digunakan: embed use di modul utama (yang Anda jalankan go build di)?

Secara pribadi, dalam konteks file statis yang tertanam secara permanen dalam biner, hashing tampaknya lebih unggul daripada stempel waktu dalam segala hal (kecuali dukungan lama dari sebelum ETag ada, pra-HTTP/1.1, tahun 90-an), dan tidak memiliki kemungkinan sumber kebingungan dalam sistem terdistribusi dan build bersamaan. Saya sangat meragukan klien yang tidak mengerti ETag ada lagi, dan mereka yakin tidak akan melakukan transisi HTTP/2.

Saluran samping untuk mengomunikasikan hash terdengar seperti hal yang benar bagi saya; antarmuka FS opsional untuk mengembalikan hash file. (Dan mungkin yang lain untuk meminta algoritme hash tertentu, jika mereka memilikinya?. Beberapa kasus penggunaan penyajian benar-benar membutuhkan sha256/sha1/md5, bukan hanya "beberapa hash"). FS yang tidak memiliki jawaban yang cukup murah dapat memilih untuk tidak mengimplementasikannya.

(Meskipun hash modern adalah gigabyte/detik/inti bahkan ketika secara kriptografis aman, puluhan gigabyte/detik untuk yang kurang aman, dan mudah di-cache berdasarkan panggilan stat. Cukup dukung ETag di mana saja, kawan.)

Saya sudah memikirkan proposal ini hari ini. Saya senang melihat fungsi ini disertakan dalam rantai alat Go, dan saya menghargai semua pemikiran dan upaya yang telah dilakukan dalam proposal hingga saat ini. Terima kasih telah mengusulkan ini dan mendorong ini ke depan.

Saya punya dua pertanyaan, dan kemudian satu saran (menghargai ini sudah melewati periode diskusi proposal, dan sudah memasuki pembekuan rilis):

Jenis yang diizinkan

Saya perhatikan bahwa kode saat ini mengharapkan variabel dideklarasikan dengan tepat ~ embed.FS ~, string , atau []byte . Secara khusus, ini tidak diperbolehkan: []uint8 , ~ FS setelah dot-import dari "embed"~, atau tipe identik lainnya yang dibuat menggunakan alias tipe. (Sunting: Saya lupa cara kerja impor titik dalam gc, dan salah membaca kode untuk mendeteksi embed.FS .)

Apakah ini disengaja atau bug?

Semantik dari []byte -variabel yang diketik.

Saya tidak melihat penyebutan apa semantik identitas yang diharapkan dari variabel yang diketik []byte . Khususnya, untuk variabel cakupan fungsi. Ini tidak penting untuk string - dan embed.FS -variabel yang diketik, karena mereka merujuk pada data yang tidak dapat diubah yang dapat dideduplikasi dengan aman. Tetapi penting untuk mengetahui semantik yang dimaksudkan untuk variabel yang diketik []byte .

Dengan implementasi saat ini, program pengujian di bawah ini mencetak false true (ketika foo.txt tidak kosong). Apakah itu dimaksudkan/dijamin?

package main

//go:embed foo.txt
var a []byte

//go:embed foo.txt
var b []byte

func f() *byte {
    //go:embed foo.txt
    var x []byte
    return &x[0]
}

func main() {
    println(&a[0] == &b[0], f() == f())
}

Saya pikir semakin banyak semantik Go-like untuk variabel //go:embed adalah dari literal komposit: bahwa setiap eksekusi menghasilkan salinan baru.

Jika tidak ada konsensus tentang semantik yang tepat untuk ini, kita selalu dapat memasukkannya dan membuat cakupan fungsi, []byte -typed menyematkan kesalahan untuk Go 1.16: pengguna masih dapat mendeklarasikan variabel tingkat paket jika mereka menginginkan semantik saat ini (satu irisan byte per deklarasi sumber), atau gunakan variabel string -typed dan konversikan ke []byte jika mereka menginginkan semantik literal komposit. Kami kemudian dapat meninjau kembali perilaku mana yang akan lebih diuntungkan oleh pengguna.

Menghindari lebih banyak arahan //go:

Saya merekomendasikan untuk tidak menambahkan arahan //go: untuk pengguna akhir yang memengaruhi semantik program, dan saya tidak menemukan argumen yang diberikan untuk mendukung //go:embed daripada sintaks Go normal yang menarik. Saya dengan hormat mendorong untuk mempertimbangkan kembali keputusan ini sebelum rilis Go 1.16. (Sekali lagi, saya menghargai betapa terlambatnya permintaan ini.)

Saya akan mulai dengan menunjukkan bahwa saya memiliki CL proof-of-concept yang berfungsi di

//go:embed foo.txt bar.txt
var x embed.FS

ke

var x = embed.Files("foo.txt", "bar.txt")

Demikian pula, fungsi embed.Bytes dan embed.String tersedia untuk menyematkan satu file dan mendapatkannya sebagai nilai []byte atau string .

Tanggapan

Demikian pula, variabel embed.Files dapat berupa variabel global atau lokal, tergantung pada konteks yang lebih nyaman.

Memiliki embed.Files , dll. memungkinkan juga menggunakannya dalam konteks ekspresi, yang mungkin masih lebih nyaman.

Ini adalah kesalahan untuk menggunakan //go:embed dalam file sumber yang tidak mengimpor "embed" (satu-satunya cara untuk melanggar aturan ini melibatkan tipuan jenis alias).

Dengan embed.Files , dll., ini adalah kesalahan berdasarkan fungsi yang diekspor oleh paket embed.

Goimports (dan gopls dll) dapat mempelajari aturan ini dan secara otomatis menambahkan impor dalam file apa pun dengan //go:embed sesuai kebutuhan.

Tidak diperlukan logika goimports atau gopls khusus untuk mengetahui bahwa mengimpor "embed" adalah cara yang tepat untuk memperbaiki penggunaan embed.Files , dll.

Pendekatan ini memperbaiki masalah pengecekan tipe—ini bukan perubahan bahasa penuh—tetapi masih memiliki kompleksitas implementasi yang signifikan.

Khususnya, CL 276835 adalah penghapusan kode secara bersih. Secara khusus, kode kompiler (yang harus diimplementasikan kembali di gccgo dan kompiler lainnya) jauh lebih sederhana.

Saya juga berharap akan lebih mudah untuk mengajarkan go/types tentang semantik bernuansa embed.Files , dll. (yaitu, bahwa mereka hanya menerima argumen literal string), daripada mengajarkannya tentang //go:embed .

Perintah go perlu mengurai seluruh file sumber Go untuk memahami file mana yang perlu disediakan untuk penyematan. Hari ini hanya mem-parsing ke blok impor, tidak pernah ekspresi Go penuh

Dengan CL 276835, perilakunya sama seperti di tip: perintah go mem-parsing seluruh file sumber Go untuk file yang mengimpor paket yang disematkan, dan hanya impor untuk file yang tidak.

Memang, untuk file yang mengimpor embed, CL 276835 melakukan parse dan walk penuh, sedangkan tip melakukan pemindaian string yang lebih efisien untuk komentar //go:embed . Saya pikir algoritma satu-pass yang lebih dioptimalkan untuk menemukan panggilan embed.Files dapat dilakukan jika diinginkan.

Juga tidak jelas bagi pengguna batasan apa yang ditempatkan pada argumen untuk panggilan khusus ini: mereka terlihat seperti panggilan Go biasa tetapi mereka hanya dapat mengambil literal string, bukan string yang dihitung oleh kode Go, dan mungkin bahkan tidak bernama konstanta (atau perintah go akan membutuhkan evaluator ekspresi Go penuh).

Ini tampaknya tidak jauh berbeda dari direktif //go:embed bagi saya: pengguna tidak memiliki harapan argumen apa yang boleh mereka gunakan di sana sampai mereka menggunakannya pertama kali. Selain itu, bagaimanapun, pengguna akan mendapatkan pesan kesalahan kompiler jika mereka menggunakannya secara tidak benar, tetapi IDE dan alat lain akan secara otomatis memberikan godocs dan referensi silang yang lebih baik untuk embed.Files , dll.

@mdempsky FS setelah dot-import embed adalah tipe yang sama. Jadi, kasus khusus itu tampaknya langsung "ya, tidak apa-apa" (sudah mencobanya dan sudah berhasil). Demikian juga, byte adalah alias untuk uint8 , jadi []uint8 juga bertipe sama dengan []byte , meskipun agak mengejutkan, ini tidak berfungsi sekarang . Namun, saya berpendapat bahwa semantik yang diterapkan baik-baik saja, untuk saat ini - kami selalu dapat mengizinkan lebih banyak tipe dan/atau alias dan/atau bahkan "tipe dasar yang sama" nanti, jika diperlukan.

Saya pikir semakin banyak semantik Go-like untuk variabel //go:embed adalah dari literal komposit: bahwa setiap eksekusi menghasilkan salinan baru.

Saya cenderung setuju, itu juga harapan saya. Pada awalnya saya pikir ini mungkin merupakan artefak analisis pelarian dan kompiler menyadari bahwa Anda tidak mengubah data, tetapi tidak:

func f() {
    //go:embed foo.txt
    var x []byte
    fmt.Printf("%q\n", x)
    x[0] = 'x'
}

func main() {
    f()
    f()
}

Namun, "setiap deklarasi var membuat variabel baru" juga memiliki masalah, karena artinya kode seperti ini

func ServeIndex(w http.ResponseWriter, r *http.Request) {
    //go:embed "index.html"
    var x []byte
    http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader(x))
}

akan sia-sia mengalokasikan dan menyalin. Mungkin itu bagus dan sebanding dengan manfaat semantik yang lebih jelas. Tapi mungkin itu juga pertanda untuk melarang penyematan []byte seperti yang Anda sebutkan.

Saya juga berharap akan lebih mudah untuk mengajarkan go/types tentang semantik bernuansa embed.Files, dll. (yaitu, bahwa mereka hanya menerima argumen literal string), daripada mengajarkannya tentang //go:embed.

Setidaknya hanya konstanta string, bahkan dapat dilakukan di sistem tipe Go.

Tapi, apakah go/types perlu tahu tentang file yang disematkan, dengan //go:embed ? Bagaimanapun juga, jenis variabel tersebut cukup jelas (dengan pengecualian penyematan []byte , seperti yang telah dibahas).

//go:embed "foo"
var x []byte

Apakah ini benar-benar dimaksudkan untuk berubah? Saya tidak dapat memikirkan satu kasus penggunaan di mana saya ingin menyematkan aset statis, tetapi mengubahnya saat runtime, jadi saya merasa itu hanya akan mengaktifkan bug. Saya akan lebih senang memasukkannya ke .rodata dan panik jika ada yang mencoba mengubahnya.

(Diminta oleh x[0] = 'x' atas.)

@Merovius menulis:

@mdempsky FS setelah dot-import embed adalah tipe yang sama. Jadi, kasus khusus itu tampaknya langsung "ya, tidak apa-apa" (sudah mencobanya dan sudah berhasil).

Terima kasih. Saya lupa cara kerja impor titik di dalam kompiler, jadi saya salah membaca kodenya. Saya seharusnya mencoba terlebih dahulu sebelum memasukkan contoh itu.

Namun, saya berpendapat bahwa semantik yang diterapkan baik-baik saja, untuk saat ini - kami selalu dapat mengizinkan lebih banyak tipe dan/atau alias dan/atau bahkan "tipe dasar yang sama" nanti, jika diperlukan.

[]byte dan []uint8 adalah tipe yang identik di bawah spesifikasi Go, seperti banyak tipe lain yang dibuat menggunakan alias tipe. Jika tidak dapat diterima bagi kompiler untuk memperlakukan embed.Files("foo" + ".txt") berbeda dari embed.Files("foo.txt") , maka argumen yang sama harus diperluas untuk tidak mengizinkannya memperlakukan tipe alias secara berbeda.

Tapi, apakah go/types bahkan perlu tahu tentang file yang disematkan, dengan //go:embed ?

Tidak, tidak perlu, sama seperti tidak perlu tahu tentang semantik khusus embed.Files juga. Tetapi untuk kedua sintaks tersebut, mungkin masih ada nilai dalam go/types yang dapat memperingatkan tentang penyalahgunaan.

--

@tv42 menulis:

Apakah ini benar-benar dimaksudkan untuk berubah?

Ternyata. Compiler Go secara khusus mengatur embed string dan embed.FS untuk dideduplikasi dan dimasukkan ke dalam rodata, sedangkan embed []byte tidak:

https://github.com/golang/go/blob/56b783ad94f9a55163d494d5b34c783a9603d478/src/cmd/compile/internal/gc/embed.go#L224

Namun, memang benar bahwa proposal tersebut tampaknya tidak membahas hal ini.

Terima kasih atas komentar yang bijaksana. Saya telah mengajukan dua masalah untuk ditindaklanjuti:

#43216 - hapus dukungan untuk embedding directives pada variabel lokal
#43217 - tentukan alias tipe String dan Bytes yang harus digunakan dengan //go:embed

Saya tidak mengajukan masalah untuk sintaks //go:embed itu sendiri. Saya ingin mengatakan mengapa di sini, sehingga semua orang tidak terlihat seperti saya hanya mengabaikannya.

Kembali ketika saya membuat blog tentang proses proposal, saya menghabiskan waktu lama mencari cara untuk (dan tidak) menangani keputusan dalam kelompok besar. Satu sumber yang menurut saya sangat masuk akal bagi saya adalah posting “ Pengambilan Keputusan Terbuka ” John Ousterhout. Seluruh posting layak dibaca, tetapi saya hanya akan mengutip di sini bagian tentang Pertimbangan Ulang:

Aturan terakhir untuk pengambilan keputusan terbuka adalah memastikan Anda tidak mempertimbangkan kembali keputusan kecuali ada informasi baru yang signifikan. Aturan ini memiliki dua bagian. Pertama, Anda harus siap untuk mengoreksi keputusan yang ternyata salah secara signifikan. Hal ini terutama berlaku di perusahaan rintisan: banyak keputusan harus dibuat tanpa informasi yang lengkap dan pasti beberapa di antaranya akan salah. Sebuah keputusan yang salah yang dikoreksi dengan cepat menyebabkan sedikit atau tidak ada kerusakan, tetapi keputusan yang salah yang tidak dikoreksi dapat menjadi bencana besar.

Di sisi lain, Anda tidak boleh mempertimbangkan kembali keputusan kecuali informasi baru yang signifikan telah terungkap sejak keputusan awal dibuat. Jika tidak ada informasi baru yang tersedia, maka mempertimbangkan kembali keputusan mungkin akan menghasilkan hasil yang sama dengan keputusan awal, membuang-buang waktu semua orang. Bukan hal yang aneh bagi karyawan untuk datang kepada saya berminggu-minggu setelah keputusan dibuat: "John, saya memilih menentang keputusan ke XYZ, dan semakin saya memikirkannya, semakin yakin saya bahwa itu salah; saya benar-benar berpikir kita perlu melakukannya. pertimbangkan kembali ini." Jawaban saya adalah "Informasi baru apa yang Anda miliki?" Jika jawabannya "tidak ada", kami tidak mempertimbangkan kembali. Dalam situasi seperti ini, akan membantu jika Anda memiliki catatan argumen yang disajikan selama diskusi, sehingga Anda dapat memverifikasi bahwa informasi baru benar-benar baru. Jika Anda membuatnya terlalu mudah untuk membuka kembali keputusan, Anda akhirnya akan terombang-ambing di mana tidak ada keputusan yang final dan karyawan ragu untuk menerapkan keputusan karena mereka tidak percaya bahwa keputusan itu permanen.

Jika Anda ingin memastikan bahwa Anda tidak mempertimbangkan kembali terlalu banyak keputusan, pastikan untuk mengumpulkan masukan secara luas selama proses pengambilan keputusan. Jika Anda tidak mendapatkan masukan yang cukup, Anda meningkatkan kemungkinan bahwa masukan baru yang signifikan akan muncul setelah keputusan, yang berarti Anda harus mempertimbangkan kembali. Jika Anda melakukan pekerjaan dengan baik dalam mengumpulkan masukan, kecil kemungkinan Anda harus meninjau kembali keputusan Anda.

Ini benar-benar selaras dengan saya: proses proposal Go (seperti proyek open source besar pada umumnya) adalah sistem dengan beban yang ditawarkan jauh lebih tinggi daripada kapasitas kerja, jadi penting bagi kita (tidak setuju jika perlu dan) berkomitmen dan beralih ke keputusan berikutnya .

string dan []byte ditambahkan cukup terlambat dalam proses mempertimbangkan masalah ini, sebagai tanggapan atas umpan balik awal, dan kami jelas tidak memikirkan semua implikasinya. Jadi saya mengajukan dua edisi baru itu, #43216 dan #43217.

Di sisi lain, sintaks //go:embed adalah bagian inti dari diskusi asli dan dibahas secara luas, baik pro maupun kontra. Saya tidak percaya ada "informasi baru yang signifikan" yang akan menyebabkan kita untuk mempertimbangkan kembali sintaks itu sepenuhnya, jadi demi kepentingan bergerak maju dan mengingat saran Ousterhout tentang Pertimbangan Ulang, saya mengesampingkannya.

Sekali lagi terima kasih telah menunjukkan masalah string dan []byte!

Ternyata. Compiler Go secara khusus mengatur embed string dan embed.FS untuk dideduplikasi dan dimasukkan ke dalam rodata, sedangkan embed []byte tidak:

@rsc apakah Anda memikirkan poin terakhir ini secara kebetulan? Dengan implementasi saat ini, mungkin menjadi "praktik terbaik" untuk menggunakan string alih-alih []byte demi menghasilkan binari yang lebih baik. Apakah kita baik-baik saja dengan itu?

Saya tidak mengerti mengapa itu akan menjadi "praktik terbaik". Bagi saya, itu mirip dengan mengatakan itu "praktik terbaik" untuk membuat kesalahan sentinel konstan, jadi kita tidak boleh mengizinkan variabel cakupan paket dari kesalahan tipe - karena saya tidak setuju dengan itu menjadi praktik yang baik dan dengan pembatasan tambahan sebagai larutan.

Saya bisa melihat argumen untuk hanya menggunakan string di vars lokal. Tetapi dalam variabel cakupan paket, semantiknya jelas dan terdefinisi dengan baik dan saya tidak akan merekomendasikan untuk tidak menggunakan embed []byte lagi daripada yang saya sarankan untuk tidak menggunakan variabel []byte .

@mvdan , jika orang bias menggunakan string alih-alih []byte sebagai praktik terbaik, saya akan menganggap itu sebagai hal yang baik. string adalah tipe Go yang sesuai untuk “string byte yang tidak dapat diubah”, sedangkan []byte adalah tipe yang sesuai untuk “string byte yang dapat berubah” atau “string byte yang dapat berubah tidak dapat ditentukan”.

Untuk kasus di mana Anda memiliki nilai tipe string (tidak dapat diubah) dan ingin menggunakannya sebagai tipe []byte (tidak dapat ditentukan), Anda sudah dapat menggunakan unsafe untuk melakukannya dengan benar . (Lihat, misalnya, unsafeslice.OfString ). Mungkin kita harus menambahkan perpustakaan standar yang didukung untuk operasi itu, tetapi itu sepertinya proposal terpisah.

Jadi sepertinya baik-baik saja untuk selalu menggunakan tipe string jika Anda benar-benar menginginkan nilainya menjadi hanya-baca.

@Merovius @bcmils Anda meningkatkan poin bagus, dan untuk lebih jelasnya saya tidak keberatan. Saya hanya ingin memastikan para perancang proposal memikirkan perbedaan ini sebelum rilis final.

Saya benar-benar tidak berpikir deduplikasi akan banyak muncul dalam praktik. (Dalam pengaturan apa beberapa paket akan menyematkan file yang sama persis?) Orang harus menggunakan formulir yang mereka butuhkan dan tidak khawatir tentang "string berarti binari saya lebih kecil", karena secara umum saya tidak berpikir itu akan benar.

Beberapa orang bertanya tentang ETag. Kami kehabisan waktu untuk itu, tetapi saya telah mengajukan proposal di https://github.com/golang/go/issues/43223 dan mudah-mudahan itu akan mengarah pada ide bagus yang bisa masuk ke Go 1.17. Maaf karena tidak bisa mendapatkannya di babak ini.

Terima kasih telah mengajukan #43216 dan #43217. Jika itu diterima, mereka akan secara substansial mengatasi masalah saya yang belum terselesaikan dengan proposal //go:embed .

Di sisi lain, sintaks //go:embed adalah bagian inti dari diskusi asli dan dibahas secara luas, baik pro maupun kontra. Saya tidak percaya ada "informasi baru yang signifikan" yang akan menyebabkan kita untuk mempertimbangkan kembali sintaks itu sepenuhnya, jadi demi kepentingan bergerak maju dan mengingat saran Ousterhout tentang Pertimbangan Ulang, saya mengesampingkannya.

Saya dapat menghormati tidak ingin meninjau kembali hal-hal yang telah diputuskan melalui diskusi yang luas. Tetapi setelah meninjau diskusi yang terjadi pada #35950, utas Reddit , dan di sini, saya rasa mereka tidak membenarkan keputusan untuk menggunakan //go:embed .

Berikut adalah komentar yang saya temukan yang menyentuh sintaks untuk menunjukkan file apa yang akan disematkan:


18 Masalah GitHub dan komentar Reddit

  • https://github.com/golang/go/issues/35950#issuecomment -561443566 "Pendekatan //go:embed memperkenalkan tingkat kerumitan lain. Anda harus menguraikan komentar ajaib untuk bahkan mengetikkan kode. Pendekatan "paket tersemat" tampaknya lebih ramah untuk analisis statis." (Catatan: Usulan //go:embed direvisi memang mempermudah mengetikkan kode cek, tetapi masih tidak sepele jika penganalisis ingin benar-benar melihat string yang digunakan, karena string tersebut tidak aktif/ast.)
  • https://github.com/golang/go/issues/35950#issuecomment -561450136 "Itu argumen yang sangat kuat untuk menggunakan paket. Itu juga membuatnya lebih mudah dibaca & didokumentasikan, karena kami dapat mendokumentasikan semuanya dengan godoc biasa, bukan daripada jauh di dalam dokumen cmd/go." (Catatan: Saya pikir ini sebagian besar ditangani oleh #43217, karena jenis penyematan paket memberikan titik referensi yang mudah.)
  • https://github.com/golang/go/issues/35950#issuecomment -561840094 "Apakah ada alasan mengapa itu tidak dapat menjadi bagian dari go build / link... misalnya go build -embed example=./path/example.txt dan beberapa paket yang terbuka akses ke sana (misalnya embed.File("example") , alih-alih menggunakan go:embed ?" (Menyarankan sintaks fungsi lebih dari //go: directive. Downvoted, tapi saya curiga karena menyarankan go build bendera.)
  • https://github.com/golang/go/issues/35950#issuecomment -561726107 "Saya bukan penggemar komentar yang mengkompilasi kode, tetapi saya juga menemukan paket semu yang memengaruhi kompilasi menjadi sedikit aneh juga. Jika pendekatan direktori tidak digunakan, mungkin akan sedikit lebih masuk akal untuk memiliki semacam deklarasi tingkat atas yang tertanam dalam bahasa tersebut. Ini akan bekerja sama dengan mengimpor, tetapi hanya akan mendukung jalur lokal dan akan membutuhkan nama untuk ditugaskan." (Tidak suka //go: atau fungsi bawaan baru, tetapi masih lebih suka kode daripada komentar.)
  • https://github.com/golang/go/issues/35950#issuecomment -561856033 "Juga, karena arahan //go:generate tidak berjalan secara otomatis saat go build, perilaku go build mungkin tampak sedikit tidak konsisten: //go:embed akan bekerja secara otomatis tetapi untuk //go:generate Anda harus menjalankan go generate secara manual ( //go:generate sudah dapat menghentikan aliran go get jika menghasilkan file .go yang diperlukan untuk build). " (Disertakan untuk kelengkapan, tetapi kita sudah memiliki //go: arahan yang keduanya mempengaruhi dan tidak mempengaruhi perilaku go build bahkan tanpa //go:embed , jadi saya tidak menemukan argumen ini menarik.)
  • https://github.com/golang/go/issues/35950#issuecomment -562005821 "Saya akan memilih paket baru sebagai lawan dari arahan. Jauh lebih mudah untuk dipahami, lebih mudah untuk ditangani/dikelola dan lebih mudah untuk mendokumentasikan dan memperluas, misalnya Dapatkah Anda dengan mudah menemukan dokumentasi untuk direktif Go seperti "go:generate"? Bagaimana dengan dokumentasi untuk paket "fmt"? Apakah Anda melihat ke mana saya akan pergi dengan ini?" (Sekali lagi, sebagian besar ditangani oleh #43217.)
  • https://github.com/golang/go/issues/35950#issuecomment -562200553 "Satu argumen mendukung pragma komentar (//go:embed) alih-alih nama direktori khusus (static/): komentar memungkinkan kami menyematkan file dalam arsip pengujian untuk sebuah paket (atau arsip xtest) tetapi bukan perpustakaan yang sedang diuji. Komentar hanya perlu muncul dalam file _test.go." (Catatan: Argumen menentang direktori khusus, bukan untuk //go:embed secara khusus; argumen yang sama meluas ke fungsi embed.Files , dll.)
  • https://github.com/golang/go/issues/35950#issuecomment -562235108 "Saya suka pendekatan package embed karena menggunakan sintaks Go"
  • https://github.com/golang/go/issues/35950#issuecomment -562713786 "Pendekatan import "C" telah menjadi preseden untuk jalur impor "ajaib". IMO ini berhasil dengan cukup baik."
  • https://github.com/golang/go/issues/35950#issuecomment -562768318 "Banyak orang telah berbicara tentang menyediakan API secara otomatis untuk membaca file yang disematkan, alih-alih membutuhkan sinyal lain seperti komentar ajaib. Saya pikir itu seharusnya menjadi cara untuk pergi, karena menawarkan sintaks program yang akrab untuk pendekatan. Pergi untuk paket khusus, mungkin runtime/embed seperti yang disebutkan sebelumnya, akan memuaskan itu dan itu akan memungkinkan untuk diperpanjang dengan mudah di masa depan."
  • https://github.com/golang/go/issues/35950#issuecomment -562959330 "Alih-alih menggunakan kembali komentar (yang akhirnya tersebar di semua tempat, dan, pada catatan pribadi, hanya merasa kotor), [... ]." (Terus menyarankan menggunakan file go.mod untuk menghitung file yang disematkan. Agar adil, juga berpendapat "Ini mengatasi masalah seperti harus membatasi paket ajaib untuk hanya mengizinkan string literal sebagai argumen, tetapi berarti lebih sulit untuk memeriksa apakah aset tertanam sebenarnya digunakan di mana saja atau akhirnya menjadi bobot mati.")
  • https://github.com/golang/go/issues/35950#issuecomment -562966654 "Ide lain: Alih-alih menambahkan jenis kata kerja baru yang disematkan di go.mod, kami dapat memperkenalkan jenis paket baru, paket data , yang diimpor dan digunakan di go.mod dengan cara biasa. Ini sketsa manusia jerami." (Preferensi untuk kode daripada komentar.)
  • https://github.com/golang/go/issues/35950#issuecomment -563156010 "Ini adalah sesuatu yang belum disebutkan: API untuk alat pemrosesan sumber Go (kompiler, penganalisis statis) sama pentingnya dengan API runtime API semacam ini adalah nilai inti dari Go yang membantu menumbuhkan ekosistem (seperti go/ast / go/format dan go mod edit ). [...] Dalam kasus paket khusus, saya tidak melihat perubahan apa pun dalam penguraian go.mod ( go mod alat) atau go/ast pengurai."
  • https://github.com/golang/go/issues/35950#issuecomment -601748774 Menyarankan daftar file sumber daya go.res. Menggunakan kode, bukan komentar.
  • https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fyv9gxw/ "Mengapa kita memerlukan komentar //go:embed misalnya binclude tidak binclude.Include(filename) untuk memasukkan file/ direktori apa tentang fs.Embed(filename) ?"
  • https://github.com/golang/go/issues/41191#issuecomment -686625127 "Menurut pendapat saya, menambahkan gula sintaksis dalam bahasa untuk mendukung perubahan perkakas ini adalah perubahan bahasa. Saya yakin ini jelas bagi orang lain, tapi ini adalah komentar-sebagai-kode yang efektif. Saya sangat merasa bahwa keajaiban/gula mengurangi kesederhanaan dan keterbacaan bahasa; sangat mudah untuk melewatkan komentar ajaib yang menyematkan file. "
  • https://github.com/golang/go/issues/41191#issuecomment -690423900 "Saya memiliki kekhawatiran yang sama dengan @tristanfisher . Go telah menggunakan komentar ajaib sebagai arahan kompiler untuk waktu yang lama (sejak awal?), tetapi mereka dimaksudkan untuk kasus sudut dan jarang muncul dalam kode. Mengingat popularitas penyematan konten statis di binari Go, //go:embed kemungkinan akan lebih umum. Mungkin sudah waktunya untuk mempertimbangkan sintaks yang berbeda untuk kompiler arahan?"
  • https://github.com/golang/go/issues/41191#issuecomment -690522509 "Saya berbagi kekhawatiran Anda tentang komentar ajaib."

Saat saya membaca komentar, mereka tampaknya selalu menyukai sintaks Go, dengan peringatan ingin menghindari perubahan yang akan merusak alat. Saya tidak melihat siapa pun berdebat untuk //go:embed sebagai ejaan, sebanyak hanya menerimanya apa adanya. Menambahkan intrinsik kompiler baru dengan stub kompatibilitas mundur untuk pemeriksa tipe lawas tampaknya lebih sesuai dengan preferensi peserta diskusi yang diungkapkan daripada lebih banyak arahan //go: .

Dalam gaya jajak pendapat jempol ke atas / jempol ke bawah di #43216 dan #43217:

  • Jempol (👍) jika Anda lebih suka intrinsik kompiler baru (mis., CL 276835 ):

    import "embed"
    
    var website = embed.Files("logo.jpg", "static/*")
    

    Lihat embed_test.go untuk contoh penggunaan lainnya.

    (Catatan: Argumen harus string literal , bukan hanya nilai string. Artinya, konstanta string yang dideklarasikan seperti const logo = "logo.jpg" atau ekspresi string konstan seperti "logo" + ".jpg" juga tidak diperbolehkan. Namun, embed.Files , dll. dapat digunakan dalam konteks apa pun, tidak hanya untuk menginisialisasi variabel.)

  • Jempol ke bawah (👎) jika Anda lebih suka arahan kompiler baru (yaitu, proposal saat ini):

    import "embed"
    
    //go:embed logo.jpg static/*
    var website embed.FS
    

@mdempsky Saya merasa Russ sudah cukup jelas, apa yang diperlukan untuk membenarkan pembalikan keputusan kepadanya - informasi baru. Saya pikir mengumpulkan komentar sebelumnya jelas bukan itu. Tidak ada pelanggaran yang dimaksudkan.

Tidak ada preseden untuk jenis fungsi baru ini, bukan? Yaitu, sesuatu yang terlihat seperti fungsi paket normal tetapi sebenarnya adalah jenis bawaan khusus yang hanya dapat dipanggil dengan cara tertentu?

Anda mengatakan "intrinsik" tetapi intrinsik saat ini berperilaku persis seperti fungsi Go normal.

x := 10000
_ = bits.RotateLeft64(10, x)

Mendandani arahan baru agar terlihat seperti fungsi Go tetapi memiliki sintaks yang berbeda (lebih ketat) daripada fungsi Go sepertinya pilihan yang buruk dari tempat saya duduk. Saya pikir embed perlu dijelaskan dalam spesifikasi, untuk satu hal, tidak seperti arahan //go: .

(Anda dapat memperkirakan "hanya argumen literal yang diizinkan" dalam kode Go normal dengan membuat fungsi yang mengambil argumen type internalString string yang tidak diekspor, tetapi menurut saya bukan itu yang Anda usulkan karena CL Anda membuat perubahan pada pengurai dan pemeriksa tipe.)

@Merovius Menurut proses proposal Go:

Konsensus dan Ketidaksepakatan

Tujuan dari proses proposal adalah untuk mencapai konsensus umum tentang hasil pada waktu yang tepat.

Jika konsensus umum tidak dapat dicapai, kelompok peninjau proposal memutuskan langkah berikutnya dengan meninjau dan mendiskusikan masalah dan mencapai konsensus di antara mereka sendiri. Jika bahkan konsensus di antara kelompok peninjau proposal tidak dapat dicapai (yang akan sangat tidak biasa), arbiter (rsc@) meninjau diskusi dan memutuskan langkah selanjutnya.

Dalam membaca komentar saya, konsensus tampaknya mendukung sintaks kode Go. Jika konsensus sekarang sebenarnya tetap dengan //go:embed , saya menghormati itu. Tetapi saya tidak berpikir proses yang terdokumentasi membenarkan keputusan awal untuk bergerak maju dengan //go:embed .

(Saat ini, hasil jajak pendapat kurang mendukung fungsi baru daripada arahan baru, tetapi tidak banyak. Jika jempol ke atas tidak melebihi jumlah jempol ke bawah setidaknya 2:1, saya baik-baik saja dengan menjatuhkan ini.)

@cepare

Tidak ada preseden untuk jenis fungsi baru ini, bukan? Yaitu, sesuatu yang terlihat seperti fungsi paket normal tetapi sebenarnya adalah jenis bawaan khusus yang hanya dapat dipanggil dengan cara tertentu?

Ada fungsi yang dideklarasikan sebelumnya di alam semesta dan fungsi paket yang tidak aman.

Anda dapat berargumen bahwa paket tidak aman didokumentasikan dalam spesifikasi Go, tetapi menurut saya itu tidak perlu. Go digunakan untuk mendukung mode di mana paket tidak aman tidak tersedia untuk pengguna, dan bahkan saat ini paket tidak aman memiliki fungsionalitas dan batasan yang hanya didokumentasikan dalam dokumen paket, bukan dalam spesifikasi Go.

Ada juga fungsi internal dalam runtime Go yang hanya diimplementasikan oleh compiler Go. Misalnya, getg , getcallerpc , getcallersp , dan getclosureptr .

(Anda dapat memperkirakan "hanya argumen literal yang diizinkan" dalam kode Go normal dengan membuat fungsi yang mengambil argumen string internalString tipe non-ekspor,

Saya pikir itu akan menjadi tambahan yang masuk akal untuk semakin mempersempit perilaku pemeriksa tipe lama.

tapi saya rasa bukan itu yang Anda usulkan karena CL Anda membuat perubahan pada parser dan pemeriksa tipe.)

CL 276835 tidak mengubah pengurai, kecuali untuk menghapus kode pengurai baru yang ditambahkan untuk //go:embed . Itu memang mengubah pemeriksa tipe, tetapi sebanding dengan //go:embed lakukan sebelumnya.

Akan mudah untuk memperluas go/types untuk mengetahui penyematan paket, tetapi saya memilih untuk tidak untuk CL 276835 secara khusus untuk menunjukkan bahwa itu masih berfungsi (mis., cmd/vet tidak gagal pada unit test embed paket).

@mdempsky Anda mungkin tidak setuju tentang apakah konsensus tercapai pada saat itu atau tidak. Sama seperti Anda mungkin tidak setuju dengan keputusan itu sendiri. Saya tidak berpikir itu benar-benar mengubah banyak hal. Pada akhirnya, "ada konsensus" juga merupakan keputusan yang dibuat. Dan persis poin yang sama tentang memerlukan informasi baru untuk pembalikan berlaku untuk keputusan itu.

Kebutuhan untuk memuaskan setiap orang adalah vektor DDoS - baik dalam hal keputusan maupun proses pembuatannya.

FTR, pertanyaan tentang perkakas vs. perubahan bahasa telah dibahas , seperti halnya tradeoff antara "string-literal vs. string-constant" ("string-literal" membutuhkan sihir di pemeriksa tipe, "string-constant" membutuhkan pergi alat untuk melakukan pengecekan jenis - komentar tidak perlu). Jadi, sekali lagi, sebenarnya tidak ada yang baru di sini. Anda mungkin tidak setuju dengan hasil keputusan itu atau proses pembuatannya - tetapi argumen yang Anda sebutkan telah dipertimbangkan saat membuat keputusan.

Kebutuhan untuk memuaskan setiap orang adalah vektor DDoS - baik dalam hal keputusan maupun proses pembuatannya.

Saya tidak menuntut agar saya secara pribadi puas, dan saya merasa menghina bahwa Anda mencirikan posting saya seperti itu. Sebelumnya Anda juga berbicara kepada saya seolah-olah saya tidak akrab dengan bahasa Go atau compiler Go. Tolong berhenti dengan merendahkan.

Saya mencantumkan di atas banyak komentar di mana orang hampir secara universal menyatakan preferensi untuk tidak menambahkan arahan //go: , sedangkan tidak ada yang berkomentar mendukung mereka. Dengan demikian, preferensi komunitas yang diungkapkan secara berlebihan tampak bagi saya untuk mendukung sintaks Go, dan komentar saya berdebat untuk membela preferensi yang mereka nyatakan.

Namun, sebagaimana adanya, https://github.com/golang/go/issues/41191#issuecomment -747095807 memiliki lebih banyak jempol daripada jempol. Itu mengejutkan bagi saya, karena tampaknya tidak sesuai dengan semua komentar selama diskusi sebelumnya. Tetapi saya senang menerima bahwa pertanyaan telah dijawab secara langsung, dan (terutama sebagai seseorang yang akan terlibat dalam dukungan jangka panjang untuk fitur ini dalam kompiler Go) saya sekarang lebih senang mendukung //go:embed melihat bahwa itu sebenarnya preferensi masyarakat dan bukan hanya preferensi penulis proposal. Hasil ini tidak akan tercapai jika diskusi ditutup, seperti yang Anda maksudkan.

FTR, pertanyaan tentang perkakas vs. perubahan bahasa telah dibahas , seperti halnya tradeoff antara "string-literal vs. string-constant" ("string-literal" membutuhkan sihir di pemeriksa tipe, "string-constant" membutuhkan pergi alat untuk melakukan pengecekan jenis - komentar tidak perlu).

Komentar itu tidak relevan dengan argumen yang saya buat. Ejaan alternatif yang saya buat di CL 276835 memiliki properti teknis yang persis sama dengan ejaan //go:embed : alat yang perlu mengetahui file apa yang akan disematkan perlu memperbarui untuk memproses file sumber Go secara berbeda (mis. tentang penyalahgunaan //go:embed komentar atau embed.Bytes builtin fungsi), sedangkan alat yang ada dapat terus kode proses cukup tanpa khawatir tentang mereka (misalnya, pergi / jenis akan mengabaikan //go:embed komentar tetapi tidak akan mendeteksi jika itu diterapkan ke tipe variabel yang salah, dan itu dapat mengetik-periksa embed.Bytes menggunakan deklarasi rintisan tetapi tidak akan tahu untuk menolak semua panggilan yang menggunakan argumen selain string literal).

Pertanyaan apakah salah satu dari ini adalah "perubahan bahasa" lebih filosofis daripada teknis.

(Argumen Russ bahwa "apakah suatu program valid tidak berubah" di bawah proposal //go:embed juga salah. https://github.com/golang/go/issues/41191#issuecomment-747799509 memberikan contoh paket yang dulu dan valid menurut spesifikasi Go dan juga diterima oleh toolchain Go dirilis hingga Go 1.15, tetapi tidak lagi valid di Go 1.16.)

Sebagai seseorang yang memberikan 👍 proposal Matt untuk menggunakan var website = embed.Files("logo.jpg", "static/*") , Perhatian saya dengan menggunakan formulir komentar ( //go:embed logo.jpg static/* ) adalah "kemudahan penggunaan".

Misalnya, 2 contoh program ini akan menampilkan 2 hal yang berbeda, hanya karena "impor" terlewatkan:

import (
    "fmt"
)

//go:embed sample.txt
var sample string

func main() {
    fmt.Println(sample) // will print a blank line
}
import (
    "embed"
    "fmt"
)

//go:embed sample.txt
var sample string

func main() {
    fmt.Println(sample) // will print contents of sample.txt
}

Dengan memaksa pengembang untuk menggunakan impor melalui semantik bahasa, Anda meminimalkan masalah di mana file yang disematkan tidak berfungsi seperti yang diharapkan karena tidak diinisialisasi seperti yang diharapkan.

@kushieda-minori Sementara saya pikir proposal saya lebih mudah digunakan juga, contoh pertama sudah tidak dikompilasi di tip:

./x.go:7:3: //go:embed only allowed in Go files that import "embed"

Saya pikir masalah ini juga dimitigasi lebih lanjut oleh #43217, karena Anda perlu mengimpor "embed" untuk mendeklarasikan variabel tipe embed.Bytes dan embed.String . Dan kompiler (tetapi bukan go/types atau cmd/vet) juga sudah melaporkan kesalahan jika Anda menerapkan direktif //go:embed ke variabel dengan tipe yang salah.

@mdempsky , bagaimanapun, secara tidak sengaja mengkompilasi pada versi Go yang lebih lama tidak akan gagal dan dapat memberikan pengertian yang salah bahwa penyematan berfungsi.

Versi Go yang lebih lama tidak memiliki penyematan paket, jadi import "embed" akan gagal. Memang benar ada risiko bahwa pengguna dapat menulis:

package p

//go:embed foo.txt
var foo []byte

dan itu akan diterima secara diam-diam oleh Go 1.15 dan yang lebih lama. Tapi itu tidak akan diterima oleh Go 1.16 dan yang lebih baru. Setidaknya tidak ada program yang akan dikompilasi dengan Go 1.15 dan Go 1.16 dan memiliki semantik yang berbeda (setidaknya karena penyematan paket).

Saya pikir perbaikan (sebagian) yang tepat di sini adalah agar cmd/kompilasi berhenti menerima arahan //go: tidak diketahui. Misalnya, batasan lain dari proposal saat ini terkait dengan sintaks bawaan Go adalah jika Anda salah mengetik direktif //go:embed (misalnya //go:enbed , //go;embed , atau // go:embed dengan spasi), itu juga akan diabaikan secara diam-diam. (Sedangkan kesalahan ketik seperti enbed.Bytes("foo.txt") akan menyebabkan kesalahan pengecekan tipe, bahkan dengan go/types yang tidak dimodifikasi.)

Poin bagus tentang arahan go: tidak divalidasi. Jika itu ditegakkan, itu akan membantu meringankan kesalahan ketik yang sulit dikenali.

Pikiran lain yang saya miliki sekarang adalah bahwa alat saya diatur untuk menambah/menghapus impor secara otomatis sesuai kebutuhan. Jika perkakas saya kedaluwarsa, apakah saya harus melawannya agar tidak menghapus impor embed "tidak terpakai"? Saya menyadari itu terpecahkan jika saya menggunakan embed.String dll, tetapi menggunakan []byte] reguler dan string seharusnya benar-benar valid. Ini bisa membuat frustasi bagi seorang gopher baru yang memetik cuplikan web untuk mendapatkan sesuatu agar berfungsi jika mereka tidak melihat alias embed.* .

tetapi menggunakan []byte] dan string seharusnya benar-benar valid.

Itu tidak akan terjadi jika #43217 diterima. Saya sarankan membaca di #43216 dan #43217. Mereka berdua telah menerima dukungan yang sangat positif sejauh ini, dan tampaknya sangat mungkin untuk diterima oleh saya. (Saya tidak berada di komite peninjau proposal.)

Terima kasih, ketika saya membaca #43217 pertama kali, saya melewatkan kata kuncinya
"memiliki" untuk menggunakan tipe embed.* .

Saya pikir satu-satunya kekhawatiran saya yang tersisa adalah yang terakhir Anda tunjukkan.

Pada Kamis, 17 Desember 2020, 20:24 Matthew Dempsky [email protected]
menulis:

tetapi menggunakan []byte] dan string biasa seharusnya benar-benar valid.

Itu tidak akan terjadi jika #43217 https://github.com/golang/go/issues/43217 adalah
diterima. Saya sarankan membaca di #43216
https://github.com/golang/go/issues/43216 dan #43217
https://github.com/golang/go/issues/43217 . Mereka berdua telah menerima
dukungan yang sangat positif sejauh ini, dan tampaknya sangat mungkin untuk diterima
untuk saya. (Saya tidak berada di komite peninjau proposal.)


Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/golang/go/issues/41191#issuecomment-747808153 , atau
berhenti berlangganan
https://github.com/notifications/unsubscribe-auth/ADLZABJWBJX475BVYDVD6ODSVKVOTANCNFSM4QTHVTUA
.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat