ruby 为什么到处都是冻结常数?

zpjtge22  于 12个月前  发布在  Ruby
关注(0)|答案(3)|浏览(108)

我们可以很容易地从许多著名的存储库中找到这种风格,如rack,rails等。
For example in rack

PATH_INFO      = 'PATH_INFO'.freeze
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
SCRIPT_NAME    = 'SCRIPT_NAME'.freeze
QUERY_STRING   = 'QUERY_STRING'.freeze
CACHE_CONTROL  = 'Cache-Control'.freeze
CONTENT_LENGTH = 'Content-Length'.freeze
CONTENT_TYPE   = 'Content-Type'.freeze

Another examle in rails

HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
HTTP_IF_NONE_MATCH     = 'HTTP_IF_NONE_MATCH'.freeze
HTTP_IF_NONE_MATCH     = 'HTTP_IF_NONE_MATCH'.freeze

我想知道为什么这些恒定的弦被冻结了。因为它们都是常量,所以应该只有一个示例。当然,我们可以把"foo".freeze放在某个地方来引用同一个单例示例,但是人们通常会写像HTTP_IF_MODIFIED_SINCE这样的字面变量名。
所以在我看来,尽管使用#freeze,它也没有任何区别,那么为什么人们要冻结常数呢?

oxiaedzo

oxiaedzo1#

当你给一个已经初始化的常量重新赋值时,Ruby会打印一个警告,这是正确的:

FOO = 'foo'
FOO = 'bar'
# :2: warning: already initialized constant FOO
# :1: warning: previous definition of FOO was here
FOO
#=> "bar"

但是对于改变常数的值的部分没有保护。没有freeze的示例:

FOO = 'foo'
FOO[1] = '-'
FOO
#=> "f-o"

但是freeze允许保护常量的值不被更改。例如freeze

FOO = 'foo'.freeze
FOO[1] = '-'
#=> RuntimeError: can't modify frozen String
xoshrz7s

xoshrz7s2#

通常Rubyist冻结字符串字面量以使执行更快。如果在某个控制器中有一些函数调用,例如下面的函数,每个请求都会调用该函数。

log("debug")

Ruby每次都会定义一个新的垃圾字符串对象。对象分配不是自由的。它消耗内存和CPU。垃圾将在那里,直到GC收集它们。
但如果文字被冻结

log("debug".freeze)

ruby分配一次并缓存它以备后用。此外,字符串对象将是不可变的,在多线程环境中使用是安全的。
根据Matz的说法,从ruby 3.0开始,ruby将冻结所有字符串。

更新:

如果你在ruby文件的开头加上下面的注解,那么整个文件中的每个字符串都是不可变的。当你试图为多线程环境优化你的应用程序时,这是非常有帮助的。

# frozen_string_literal: true

或者你甚至可以用--enable-frozen-string-literal开关启动你的Ruby进程。

isr3a4wc

isr3a4wc3#

为什么在流行的项目中会看到这种常数的持续冻结,一个解释是它们使用了Rubocop,一种代码分析器。
这是一个标准的Rubocop rule,常量不应该是可变的,因为@spickermann上面提到的原因。

相关问题