程式碼註解

Ruby 有兩種註解:內嵌和區塊。

內嵌註解以 # 字元開頭,並持續到該行結尾

# On a separate line
class Foo # or at the end of the line
  # can be indented
  def bar
  end
end

區塊註解以 =begin 開頭,並以 =end 結尾。每個都應從單獨一行開始。

=begin
This is
commented out
=end

class Foo
end

=begin some_tag
this works, too
=end

=begin=end 無法縮排,因此這是一個語法錯誤

class Foo
  =begin
  Will not work
  =end
end

神奇註解

雖然註解通常會被 Ruby 忽略,但特殊的「神奇註解」包含影響程式碼詮釋方式的指令。

頂層神奇註解必須出現在檔案的第一個註解區段中。

注意:神奇註解只會影響它們出現的檔案;其他檔案不受影響。

# frozen_string_literal: true

var = 'hello'
var.frozen? # => true

替代語法

魔術註解可能包含單一指令(如上例所示)。或者,如果多個指令以「;」分隔並以「-*-」包住,則它們可以出現在同一行中(請參閱 Emacs 的 檔案變數)。

# emacs-compatible; -*- coding: big5; mode: ruby; frozen_string_literal: true -*-

p 'hello'.frozen? # => true
p 'hello'.encoding # => #<Encoding:Big5>

編碼指令

指出字串字面值、正規表示式字面值和 __ENCODING__ 應使用的字串編碼。

# encoding: big5

''.encoding # => #<Encoding:Big5>

預設編碼為 UTF-8。

頂層魔術註解必須從第一行開始,或者如果第一行看起來像 #! shebang line,則從第二行開始。

可以使用「編碼」一詞代替「編碼」。

凍結字串字面值指令

指出字串字面值應在解析時一次配置並凍結。

# frozen_string_literal: true

3.times do
  p 'hello'.object_id # => prints same number
end
p 'world'.frozen? # => true

預設為 false;可以使用 --enable=frozen-string-literal 變更此設定。如果沒有指令,或使用 # frozen_string_literal: false,上述範例將列印 3 個不同的數字和「false」。

從 Ruby 3.0 開始,動態的字串字面值不會凍結或重複使用。

# frozen_string_literal: true

p "Addition: #{2 + 2}".frozen? # => false

它必須出現在檔案的第一個註解區段中。

警告縮排指令

此指令可以開啟對其後陳述式錯誤縮排的偵測。

def foo
  end # => no warning

# warn_indent: true
def bar
  end # => warning: mismatched indentations at 'end' with 'def' at 6

取得這些警告顯示的另一種方法是使用警告執行 Ruby(ruby -w)。使用指令將此設定為 false 將防止這些警告顯示。

可共用常數值指令

注意:此指令在 Ruby 3.0 中為實驗性質,且未來版本可能會變更。

此特殊指令有助於建立僅包含不可變物件或 Ractor 可共用 常數的常數。

此指令可以指定對指定給常數的值進行特殊處理。

模式 none(預設)

此模式中沒有特殊處理(如 Ruby 2.x 中):沒有自動凍結和檢查。

深度凍結常數一直是個好主意;Ractor 讓這成為更好的主意,因為只有主 ractor 可以存取不可共用的常數。

# shareable_constant_value: none
A = {foo: []}
A.frozen? # => false
Ractor.new { puts A } # => can not access non-shareable objects by non-main Ractor.

模式 literal

在「literal」模式中,指定給字面值的常數將會深度凍結。

# shareable_constant_value: literal
X = [{foo: []}] # => same as [{foo: [].freeze}.freeze].freeze

其他值必須可共用。

# shareable_constant_value: literal
X = Object.new # => cannot assign unshareable object to X

請注意,只有直接指定給常數,或遞迴包含在這些字面值中的字面值才會凍結。

# shareable_constant_value: literal
var = [{foo: []}]
var.frozen? # => false (assignment was made to local variable)
X = var # => cannot assign unshareable object to X

X = Set[1, 2, {foo: []}].freeze # => cannot assign unshareable object to X
                                # (`Set[...]` is not a literal and
                                # `{foo: []}` is an argument to `Set.[]`)

方法 Module#const_set 不受影響。

模式 experimental_everything

在此模式中,指派給常數的所有值都可共用。

# shareable_constant_value: experimental_everything
FOO = Set[1, 2, {foo: []}]
# same as FOO = Ractor.make_sharable(...)
# OR same as `FOO = Set[1, 2, {foo: [].freeze}.freeze].freeze`

var = [{foo: []}]
var.frozen? # => false (assignment was made to local variable)
X = var # => calls `Ractor.make_shareable(var)`
var.frozen? # => true

此模式為「實驗性」,因為它可能會造成錯誤,例如深度凍結外部資源的常數,這可能會導致錯誤。

# shareable_constant_value: experimental_everything
FOO = SomeGem::Something::FOO
# => deep freezes the gem's constant!

這將在 Ruby 3.1 之前重新檢視,以允許「一切」或移除此模式。

方法 Module#const_set 不受影響。

模式 experimental_copy

在此模式中,指派給常數的所有值都深度複製並可共用。它比 experimental_everything 更安全的模式。

# shareable_constant_value: experimental_copy
var = [{foo: []}]
var.frozen? # => false (assignment was made to local variable)
X = var # => calls `Ractor.make_shareable(var, copy: true)`
var.frozen? # => false
Ractor.shareable?(X) #=> true
var.object_id == X.object_id #=> false

此模式為「實驗性」,尚未徹底討論。這將在 Ruby 3.1 之前重新檢視,以允許「複製」或移除此模式。

方法 Module#const_set 不受影響。

範圍

此指令可以在同一個檔案中使用多次。

# shareable_constant_value: none
A = {foo: []}
A.frozen? # => false
Ractor.new { puts A } # => can not access non-shareable objects by non-main Ractor.

# shareable_constant_value: literal
B = {foo: []}
B.frozen? # => true
B[:foo].frozen? # => true

C = [Object.new] # => cannot assign unshareable object to C (Ractor::IsolationError)

D = [Object.new.freeze]
D.frozen? # => true

# shareable_constant_value: experimental_everything
E = Set[1, 2, Object.new]
E.frozen? # => true
E.all(&:frozen?) # => true

此指令只影響後續常數,且只針對目前的範圍。

module Mod
  # shareable_constant_value: literal
  A = [1, 2, 3]
  module Sub
    B = [4, 5]
  end
end

C = [4, 5]

module Mod
  D = [6]
end
p Mod::A.frozen?, Mod::Sub::B.frozen? # => true, true
p C.frozen?, Mod::D.frozen? # => false, false