我试图理解Ruby中的.freeze概念。

时间:2018-09-05 00:43:34

标签: ruby

谁能解释一个字符串为什么会意外突变。我的导师对此进行了投标,但是我真的不明白为什么。

2 个答案:

答案 0 :(得分:3)

关于常量引用的对象如何变异的示例:

FOO = 'foo'
p FOO # => "foo"
FOO.upcase!
p FOO # => "FOO" (no errors)

BAR = 'bar'.freeze
p BAR # => "BAR"
BAR.upcase! # => freeze.rb:8:in `upcase!': can't modify frozen String (RuntimeError)

freeze常量引用的对象可以防止代码的意外行为,因此开发人员可以对其进行修复或其他任何操作。

要查看freeze仅冻结对象,请查看如果将新值分配给常量,则抛出的错误将有所不同:

BAZ = 'baz'.freeze # => warning: previous definition of BAZ was here
BAZ = 'bazbaz' # => warning: already initialized constant BAZ

受本主题启发:Why are there frozen constants everywhere?

答案 1 :(得分:1)

以下是一些使字符串变异的方法:<<gsub!downcase!replace ...

假设有一个网站“ Chitter”,用户在其中发布有关其日常生活的“ cheets”。让我们假设Chitter帐户名称是普通名称,只是小写并带有下划线而不是空格(这很愚蠢,但这是一个愚蠢的示例,因此只需滚动即可)。您想通过网站的API获取用户的清单;您想要显示“您好,Amadan!这是您的秘诀:”,然后向您展示它的发现。您环顾四周,这里有一个图书馆!太棒了因此,您gem install并开始编码:

require 'chitter'
print "What is your name? "
name = gets
chitter = Chitter.login_by_name(name)
puts "Hello, #{name}! Here are your cheets:"
puts chitter.cheets

我输入了Amadan,期望是Hello, Amadan-但出来的Hello, amadan是小写的!那怎么会发生?

似乎我们想象中的Chitter宝石在其login_by_name中有这行:

name.gsub!(' ', '_').downcase!

在这里,出现意外的字符串突变。可以肯定的是,故障完全在我身上,即假想的Chitter宝石的作者,因为如果那条线是

name = name.gsub(' ', '_').downcase

甚至

name = name.dup.gsub!(' ', '_').downcase!

不会有问题,不会因用户注资而受到侮辱。但是,如果他们的测试套件包含Chitter.login_by_name("testname".freeze),那么当这些变异者尝试进行变异时,他们的测试就会遇到异常。

同一件事可能会相反。以以下代码段为例:

require 'chitter'
print "What is your name? "
name = gets
chitter = Chitter.login_by_name(name)
best_friend = chitter.best_friend
best_friend.name.upcase!
puts "Shoutout to your BEST FRIEND, #{best_friend.name}! Here's some of their cheets:"
puts chitter.best_friend.cheets

pandemonium爆发,因为您最好的朋友突然没有Cheeter帐户!为什么?因为显然有人忘记了freeze返回的对象中的name chitter.best_friend。然后,用户用upcase!修改了该名称,然后库尝试使用大写名称访问cheet,但失败了。问题的根源是

class Cheeter::User
  def initialize(name)
    @name = name
  end
  ...
  attr_reader :name
  ...
end

如果初始化说@name = name.freeze,那么我们就不能upcase!。如果我们使用此方法而不是attr_reader

def name
  @name.dup
end

然后运行upcase!没关系,该库使用的名称不会更改。但是由于cheeter.best_friend.name恰好返回了它正在使用的对象,而没有freeze,因此在对它进行突变时,我们将猴子扳手投入了操作,认为这是我们的职责

编辑:似乎“颤抖”实际上存在。我对Chitter的创建者表示歉意,而不是重命名我的示例并冒着碰到另一个真实应用程序的风险,所有相似之处都是偶然的,而且我敢肯定,真正的Chitter并不是像我这样可怕地设计的。