冒险村11 - frozen_string_literal

11 - frozen_string_literal

延续 Begin from linter : rubocop 中最後 Auto-fix 提到 frozen_string_literal 的设定,这魔法般的注解到底代表什麽意思呢?

先从 Ruby 中常数(constant)变更来理解可以很容易看出 MY_CONSTANT constant 是可以被变更的,不过我们常常在专案中或许想要写死某个 default 值,会想要建立的是 immutable,可以透过 ruby 提供的 freeze 方法来处理。

  MY_CONSTANT = "chester"
  MY_CONSTANT << "_tang"
  puts MY_CONSTANT.inspect

  # "chester_tang"
  # => nil

  Chester = "Chester"
  Chester = "ChesterTang"
  # :2: warning: already initialized constant Chester
  # :1: warning: previous definition of Chester was here
  Chester
  # => "ChesterTang"

Creating immutable constants

  MY_CONSTANT = "chester".freeze
  MY_CONSTANT << "_tang"
  # FrozenError: can't modify frozen String: "chester"

testing performance with benchmark/ips

透过 benchmark-ips 来测试一定时间内可以执行最多的次数,从结果显示可以看出约提升 50% 的速度。

  require 'benchmark/ips'

  def noop(arg)
  end

  Benchmark.ips do |x|
    x.report("normal") { noop("foo") }
    x.report("frozen") { noop("foo".freeze) }
  end

  # Results with MRI 2.2.2:
  # Calculating -------------------------------------
  #   normal   152.123k i/100ms
  #   frozen   167.474k i/100ms
  # -------------------------------------------------
  #   normal   6.158M (± 3.3%) i/s -     30.881M
  #   frozen   9.312M (± 3.5%) i/s -     46.558M

Value objects & functional programming

也可以在 init 内使用 freeze 方法,保证 constructor 内 object 不会被改变

  class Score
    attr_accessor :a, :b
    def initialize(a, b)
      @a = a
      @b = b
      freeze
    end

    def change
      @a = 3
    end
  end

  score = Score.new(1,2)
  score.change # RuntimeError: can't modify frozen Score

Built-in optimizations in Ruby >= 2.2

在 >= 2.2 以後的 Ruby 版本中针对 hash 使用的字串已经有做自动 freeze,不过如果版本在这之前就可能会常常看到下面的这种情况:

  user = {"name" => "george"}
  # In Ruby >= 2.2
  user["name"]
  # ...is equivalent to this, in Ruby <= 2.1
  user["name".freeze]

注: Immutable String literal in Ruby 3 原本 Ruby 3.0 中自动 freeze 所有字串目前也没有後续。

最後,再回来一开始设定 frozen_string_literal 原因,到底是什麽魔法呢?

Magic Comments!

版本 >= 2.3 後的功能,这行注解起来的魔法,可以自动将该档案内的 String 通通做 #freeze 的方法

  # frozen_string_literal: true
  require "sidekiq/version"

  module Sidekiq
    NAME = "Sidekiq"
    LICENSE = "See LICENSE and the LGPL-3.0 for licensing details."
    DEFAULTS = {
      #...
    }
    #...
  end

参考来源

My blog


<<:  android studio 30天学习笔记-day 11-介绍databinding(一)

>>:  [ Day 11 ] React 的生命周期 - Mounting

Flutter学习Day5 Widget StatelessWidget => StatefulWidget 实作

大家安安 晚上好~~ 今天要把专案里的StatelessWidget 更改成为 StatefulWi...

案例:AWS MLOps Framework - 解决方案介绍

在AWS solutions library你可以找到数十份各式各样的解决方案参考文件,在这个解决方...

[30天 Vue学好学满 DAY2] Vue.js介绍

核心概念-渐进式框架(progressive framework) 一个完整的页面是由各个组件(co...

Day 24 -资料库应用小程序 资料库设计(实体关联模型(E-R model)分析)

实体关系模型 ( Entity - Relationship Model, E-R Model )是...

[第29天]理财达人Mx. Ada-布林通道(Bollinger Band)

前言 本文说明使用TA-Lib函式库计算及呈现布林通道。 布林通道 布林通道(Bollinger B...