Apa yang dilakukan oleh komentar “frozen_string_literal: true”?

226

Ini adalah rspecbinstub di direktori proyek saya.

#!/usr/bin/env ruby
begin
  load File.expand_path("../spring", __FILE__)
rescue LoadError
end
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
  Pathname.new(__FILE__).realpath)

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rspec-core", "rspec")

Apa yang ingin dilakukan?

# frozen_string_literal: true
messanjah
sumber

Jawaban:

314

# frozen_string_literal: trueadalah komentar ajaib, yang didukung untuk pertama kalinya di Ruby 2.3, yang memberi tahu Ruby bahwa semua string literal dalam file dibekukan secara implisit, seolah-olah #freezetelah dipanggil untuk masing-masingnya. Yaitu, jika string literal didefinisikan dalam file dengan komentar ini, dan Anda memanggil metode pada string yang memodifikasinya, seperti <<, Anda akan mendapatkannya RuntimeError: can't modify frozen String.

Komentar harus berada di baris pertama file.

Di Ruby 2.3, Anda dapat menggunakan komentar ajaib ini untuk mempersiapkan literal string beku sebagai default di Ruby 3 .

Di Ruby 2.3 dijalankan dengan --enable=frozen-string-literalbendera, dan di Ruby 3, string literal dibekukan di semua file. Anda dapat mengganti pengaturan global dengan # frozen_string_literal: false.

Jika Anda ingin string literal dapat berubah tanpa memperhatikan pengaturan global atau per-file, Anda dapat mengawalinya dengan +operator unary (berhati-hati dengan prioritas operator) atau memanggilnya .dup:

# frozen_string_literal: true
"".frozen?
=> true
(+"").frozen?
=> false
"".dup.frozen?
=> false

Anda juga dapat membekukan string yang tidak dapat diubah (unfrozen) dengan unary -.

Dave Schweisguth
sumber
24
Hal penting yang perlu diperhatikan tentang membekukan string adalah meningkatkan kinerja aplikasi . Lihat juga di sini
Andres Ehrenpreis
2
@ dave-schweisguth Bukankah kita -"foo"seharusnya sama dengan "foo".freeze? Ketika saya memeriksa (-"foo").__id__saya mendapat nilai yang berbeda setiap kali, tetapi "foo".freeze.__id__sama setiap kali. Ada ide?
lilole
Saya bertanya-tanya apakah fungsi ini adalah masalah, tampaknya hanya dipanggil dengan minus unary. github.com/ruby/ruby/blob/trunk/string.c#L2572
lilole
2
-adalah untuk mendupuplikasi String untuk menghemat memori, selain mengembalikan String yang beku.
eregon
9
Meskipun Anda masih dapat menggunakan komentar ajaib, Matz secara resmi memutuskan untuk tidak membuat semua string literal berubah secara default di Ruby 3: bugs.ruby-lang.org/issues/11473#note-53
Konstantin Tikhonov
44

Ini meningkatkan kinerja aplikasi dengan tidak mengalokasikan ruang baru untuk string yang sama, sehingga juga menghemat waktu untuk pekerjaan pengumpulan sampah. Bagaimana? ketika Anda membekukan string literal (objek string), Anda memberi tahu Ruby untuk tidak membiarkan program Anda memodifikasi string literal (objek).

Beberapa pengamatan jelas perlu diingat.

1. Dengan membekukan string literal, Anda tidak mengalokasikan ruang memori baru untuk itu.

Contoh:

Tanpa komentar ajaib mengalokasikan ruang baru untuk string yang sama (Amati berbagai objek ID yang dicetak)

def hello_id
  a = 'hello'
  a.object_id
end

puts hello_id   #=> 70244568358640
puts hello_id   #=> 70244568358500

Dengan komentar ajaib , ruby ​​mengalokasikan ruang hanya sekali

# frozen_string_literal: true

def hello_id
  a = 'hello'
  a.object_id
end

puts hello_id   #=> 70244568358640
puts hello_id   #=> 70244568358640

2. Dengan membekukan string literal, program Anda akan memunculkan eksepsi ketika mencoba untuk memodifikasi string literal.

Contoh:

Tanpa komentar ajaib , Anda dapat memodifikasi string literal.

name = 'Johny'
name << ' Cash'

puts name     #=> Johny Cash

Dengan komentar ajaib , pengecualian akan dimunculkan ketika Anda memodifikasi string literal

# frozen_string_literal: true

name = 'john'
name << ' cash'  #=> `<main>': can't modify frozen String (FrozenError)

puts name      

Selalu ada lagi yang harus dipelajari dan fleksibel:

imechemi
sumber
Ini adalah jawaban yang lebih intuitif.
Jin Lim
20

Di Ruby 3.0. Matz (pembuat Ruby) memutuskan untuk membuat semua String literal dibekukan secara default.

Anda dapat menggunakan di Ruby 2.x. Cukup tambahkan komentar ini di baris pertama file Anda.

# frozen_string_literal: true

Komentar di atas di atas file mengubah semantik literal string statis dalam file. Literal string statis akan dibekukan dan selalu mengembalikan objek yang sama. (Semantik string string dinamis tidak berubah.)

Cara ini memiliki manfaat sebagai berikut:

Tidak ada akhiran f-jelek. Tidak ada kesalahan sintaks pada Ruby yang lebih lama. Kami hanya perlu satu baris untuk setiap file.

Silakan, baca topik ini untuk informasi lebih lanjut.

https://bugs.ruby-lang.org/issues/8976

Alexandr
sumber
Sayangnya komentar ini tidak berfungsi untuk string dalam array, jadi mereka masih perlu dibekukan secara eksplisit
ToTenMilan
3
Sayangnya ini tidak akan berada di ruby ​​3 bugs.ruby-lang.org/issues/11473#note-53
zhisme