代码注释

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__ 应该使用的字符串编码

# encoding: big5

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

默认编码为 UTF-8。

顶级的魔术注释必须从第一行开始,如果第一行看起来像 #! shebang line,则从第二行开始。

可以使用单词“coding”代替“encoding”。

frozen_string_literal 指令

指示应该在解析时分配一次字符串字面量并将其冻结。

# 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

它必须出现在文件的第一个注释部分。

warn_indent 指令

此指令可以打开对后续语句的错误缩进的检测

def foo
  end # => no warning

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

显示这些警告的另一种方法是使用警告运行 Ruby (ruby -w)。使用指令将此设置为 false 将阻止显示这些警告。

shareable_constant_value 指令

注意:此指令在 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 之前重新审视此问题,以允许 ‘everything’ 或改为删除此模式。

方法 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 之前重新审视此问题,以允许 ‘copy’ 或改为删除此模式。

方法 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