评论“ frozen_string_literal:true”有什么用?


226

这是rspec我的项目目录中的binstub。

#!/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")

这打算做什么?

# frozen_string_literal: true

Answers:


314

# frozen_string_literal: true是一个魔术注释,在Ruby 2.3中首次受支持,它告诉Ruby文件中的所有字符串文字都被隐式冻结,就像#freeze在每个字符串上都被调用一样。也就是说,如果在带有此注释的文件中定义了字符串文字,并且您对该字符串调用了对其进行修改的方法,例如<<,则会得到RuntimeError: can't modify frozen String

注释必须在文件的第一行。

在Ruby 2.3中,您可以使用此魔术注释来准备冻结的字符串文字,这是Ruby 3中的默认值

在带--enable=frozen-string-literal标志的Ruby 2.3中运行,在Ruby 3中,字符串文字冻结在所有文件中。您可以使用覆盖全局设置# frozen_string_literal: false

如果您希望字符串文字是可变的,而不管全局设置或每个文件设置如何,都可以在前缀前使用一元运算+符(请注意运算符的优先级)或对其进行调用.dup

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

您还可以使用unary冻结可变的(未冻结的)字符串-


24
关于冻结字符串要注意的重要事项是它可以提高应用程序的性能。另请参阅此处
Andres Ehrenpreis

2
@ dave-schweisguth我们不应该期望-"foo"与之相同"foo".freeze吗?当我检查时,(-"foo").__id__我每次都得到一个不同的值,但是"foo".freeze.__id__每次都相同。有任何想法吗?
lilole

我想知道这个函数是否有问题,似乎只用一元减号调用。github.com/ruby/ruby/blob/trunk/string.c#L2572
lilole

2
-除了返回冻结的String之外,还用于对String进行重复数据删除以节省内存。
俄勒冈州

9
尽管您仍然可以使用魔术注释,但是Matz正式决定不将所有字符串文字默认情况下在Ruby 3中保持
Konstantin Tikhonov,

43

通过不为同一字符串分配新空间来提高应用程序性能,从而也节省了垃圾收集工作的时间。怎么样?当冻结字符串(字符串对象)时,是在告诉Ruby不要让任何程序修改字符串(对象)。

注意一些明显的观察。

1.通过冻结字符串文字,您无需为其分配新的内存空间。

例:

没有魔术注释,将为同一字符串分配新的空间(观察打印的不同对象ID)

def hello_id
  a = 'hello'
  a.object_id
end

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

通过魔术注释,ruby仅分配一次空间

# frozen_string_literal: true

def hello_id
  a = 'hello'
  a.object_id
end

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

2.通过冻结字符串文字,您的程序在尝试修改字符串文字时将引发异常。

例:

如果没有魔术注释,则可以修改字符串文字。

name = 'Johny'
name << ' Cash'

puts name     #=> Johny Cash

使用魔术注释,修改字符串文字时将引发异常

# frozen_string_literal: true

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

puts name      

总是有更多的东西要学习并且要灵活:


这是一个更直观的答案。
金林

20

在Ruby 3.0中。Matz(Ruby的创建者)决定默认情况下冻结所有String文字。

您可以在Ruby 2.x中使用。只需在文件的第一行中添加此注释即可。

# frozen_string_literal: true

文件顶部的以上注释更改了文件中静态字符串文字的语义。静态字符串文字将被冻结,并且始终返回相同的对象。(动态字符串文字的语义不变。)

这种方式具有以下优点:

没有难看的f后缀。旧版Ruby没有语法错误。每个文件只需要一行。

请阅读本主题以获取更多信息。

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


不幸的是,此注释不适用于数组中的字符串,因此仍然需要明确冻结它们
ToTenMilan

3
不幸的是,这不会出现在ruby 3 bugs.ruby-lang.org/issues/11473#note-53
zhisme
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.