【问题标题】:Ruby: What does the comment "frozen_string_literal: true" do?Ruby:评论“frozen_string_literal:true”有什么作用?
【发布时间】:2016-06-13 21:18:02
【问题描述】:

这是我项目目录中的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

【问题讨论】:

    标签: ruby


    【解决方案1】:

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

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

    在 Ruby 2.3 中,您可以使用这个神奇的注释为 Ruby 3 中默认的冻结字符串文字做好准备

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

    如果您希望字符串文字是可变的,而不管全局或每个文件的设置,您可以在它前面加上一元 + 运算符(注意运算符优先级)或在其上调用 .dup

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

    您还可以使用一元 - 冻结可变(未冻结)字符串。

    【讨论】:

    • 关于冻结字符串的重要注意事项是它improves app's performance。另见here
    • @dave-schweisguth 我们不应该期望-"foo""foo".freeze 相同吗?当我检查(-"foo").__id__ 时,我每次都会得到不同的值,但"foo".freeze.__id__ 每次都是相同的。有什么想法吗?
    • - 用于对字符串进行重复数据删除以节省内存,同时返回一个冻结的字符串。
    • 虽然你仍然可以使用魔术注释,但 Matz 官方决定不在 Ruby 3 中默认所有字符串文字不可变:bugs.ruby-lang.org/issues/11473#note-53
    • 我还是不明白。为什么需要它?
    【解决方案2】:

    它通过不为相同的字符串分配新空间来提高应用程序性能,从而也节省了垃圾收集杂务的时间。如何?当您冻结字符串文字(字符串对象)时,您是在告诉 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      
    

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

    【讨论】:

    • 这是一个更直观的答案。
    • 保持简单明了,对我来说是最好的答案。
    • 感谢中肯和直观的回答。
    【解决方案3】:

    在 Ruby 3.0 中。 Matz(Ruby 的创建者)决定默认冻结所有字符串文字。

    EDIT 2019:他决定放弃为 Ruby 3.0 设置默认冻结字符串文字的想法(来源:https://bugs.ruby-lang.org/issues/11473#note-53


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

    # frozen_string_literal: true
    

    文件顶部的上述注释更改了静态字符串的语义 文件中的文字。静态字符串文字将被冻结并 总是返回相同的对象。 (动态字符串字面量的语义 没有改变。)

    这种方式有以下好处:

    没有丑陋的 f 后缀。 旧 Ruby 上没有语法错误。 我们只需要一条线 每个文件。

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

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

    【讨论】:

    猜你喜欢
    • 2015-01-13
    • 1970-01-01
    • 2013-07-10
    • 1970-01-01
    • 2011-12-10
    • 2012-11-07
    • 2012-02-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多