freeze 小技巧

Published on:

前言:
不小心翻到 ihower 老师的技术博客,其中一篇:如何真正讓 Ruby Constants 常數無法被修改,很有意思,我按照自己的思路整理,记录如下。

我们知道 ruby 中 freeze 这个 method 可以用来freeze 一个 object,但实际上并不完全奏效。

string和 number 不能直接被 freeze,拿 array 举例好了:

> a = ["a", "b", "c"]
 => ["a", "b", "c"]
> a.freeze
 => ["a", "b", "c"]
> a[0] = 3
RuntimeError: can't modify frozen Array
> a[0] << "x"
 => "ax"
> a
 => ["ax", "b", "c"]

那如何才能确保 array不被修改到呢?

答案是用一个 class把 array包起来, 然后在 class 内部 freeze。

class A
  B = ["a", "b", "c"].freeze
  
  def self.mutate
    B[0] << "x"
  end
    
end

A::B << "d"
# RuntimeError: can't modify frozen Array 丟出錯誤例外,不能修改!

A.mutate
A::B # 被修改成 ["ax", "b", "c"] 了,失敗 :(

Oops,居然还是失败了。

原来freeze 容器,只能防止新增和删除元素,却不能阻止个别元素被直接修改,为了到达目的,还需要对每一个元素都freeze一下:

class A
  B = ["a", "b", "c"].map!(&:freeze).freeze
  
  def self.mutate
    B[0] << "x"
  end
    
end

A::B << "d"
# RuntimeError: can't modify frozen Array 丟出錯誤例外,不能修改!

A.mutate
# RuntimeError: can't modify frozen String 丟出錯誤例外,不能修改!

另外,对于常数只需要用 module 包起来,然后对 module 进行 freeze 就好了。

module Y
  X = 1
end

Y.freeze

Y::X = 2

# RuntimeError: can't modify frozen Module 丟出錯誤例外,不能修改!

Comments

comments powered by Disqus