Ruby on Rails中Sidekiq Worker遭遇FrozenString错误求助
为什么在Sidekiq Worker调用的Concern中会触发FrozenString错误?
这问题我之前在Rails项目里也碰到过,咱们来拆解一下背后的原因:
核心原因:Rails默认开启的冻结字符串字面量机制
从Ruby 2.3开始,引入了冻结字符串字面量的特性,目的是减少内存开销并提升性能。而Rails在新版本(5.1+)的默认配置中,会开启config.active_support.frozen_string_literal = true,同时大部分Rails文件顶部也会有# frozen_string_literal: true的魔法注释——这两个设置会让文件内所有的字符串字面量默认被冻结,也就是不可被原地修改。
你在Concern里定义的html = "<ul>"就是一个典型的字符串字面量,在开启冻结的环境下,这个字符串对象被标记为只读状态。当你调用html << "Hello"这种原地修改字符串的方法时,自然就会触发FrozenError。
为什么重新赋值后就能正常操作?
当你在Pry里重新执行html = "<ul>"时,这个字符串是在交互式会话中动态创建的。Pry默认没有启用frozen_string_literal特性,所以这个新生成的字符串对象是未冻结的,允许你用<<进行原地修改。
换个角度说:原来的html变量指向的是Concern文件中被冻结的字面量对象;重新赋值后,变量指向了一个全新的、未冻结的字符串对象,两者是完全不同的实例。
解决办法
针对这个问题,有几种常用的处理方式:
- 改用非原地修改的方式:比如用
html += "Hello"或者html = html + "Hello",这些操作会返回一个新的字符串对象,不会触碰原冻结的字面量。 - 显式创建可修改的字符串:初始化时用
html = "<ul>".dup或者html = String.new("<ul>"),这样生成的字符串从一开始就是未冻结的。 - 先复制再修改:如果必须用
<<,可以先复制原字符串:html = html.dup << "Hello",注意这会创建新对象,原冻结对象不会被改变。
内容的提问来源于stack exchange,提问作者LewlSauce
相关产品推荐
相关产品推荐

