为什么到处都是冻结常量?

问题描述 投票:0回答:3

我们可以从很多著名的存储库中轻松找到这种样式,例如机架、导轨等。

例如在机架中

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

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
,但没有任何区别,那么为什么人们要冻结常量呢?

ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-4 rack
3个回答
38
投票

当您为已初始化的常量重新赋值时,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

8
投票

通常 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 进程。


1
投票

在流行项目中看到这种一致的常量冻结的一个解释是它们使用Rubocop,一种代码分析器。

这是一个标准的 Rubocop 规则,即常量不应该是可变的,出于@spickermann 上面提到的原因。

© www.soinside.com 2019 - 2024. All rights reserved.