🏗️

Struct vs Class — 継承なしで生きる方法

Rubyのclass < Baseの代わりにGoのstruct埋め込み

Ruby開発者にとってclassは空気のような存在だ。全てがクラスの中にある。class User < ApplicationRecord — 継承で機能を引き継ぎ、includeでモジュールを混ぜる。

Goにはclassも継承もない。structがデータ構造を定義し、そのstructにメソッドを付けるのが全て。

Rubyのclass vs Goのstruct

Ruby: class User; attr_accessor :name, :email; end
Go: type User struct { Name string; Email string }

似て見えるが根本が違う。Rubyのclassはインスタンス変数、メソッド、継承、ミックスインを全て収める器。Goのstructはただのデータフィールドの束。メソッドは外から付ける。

継承がない — 埋め込みで代替

class Admin < UserはGoでは不可能。代わりにstruct内に別のstructを入れる:

type Admin struct {
    User  // 埋め込み — Userの全フィールドとメソッドを取得
    Role string
}

AdminはUserを「継承」したのではなく「包含」した。admin.NameでUserのフィールドにアクセス可能で、Userのメソッドもそのまま呼べる。

Rubyのinclude UserModuleに似ている。ただしGoの埋め込みは型レベルで起こり、ランタイムオーバーヘッドがない。

newがない

Ruby: User.new(name: "kim") — initializeが呼ばれる。Goにはコンストラクタがない。User{Name: "kim"}でリテラル生成するか、慣例的にNewUser("kim")ファクトリ関数を作る。

private/publicが大文字小文字で決まる

Rubyのprivateprotectedpublicキーワードの代わりに、Goは名前の先頭文字で決める。大文字開始 = エクスポート(外部アクセス可能)。小文字開始 = パッケージ内部用。User.Nameは外部からアクセス可能、User.nameは不可能。

RubyからGoへ

1

Ruby: class < Base 継承 → Go: struct埋め込み(包含関係)

2

Ruby: attr_accessor → Go: structフィールド(大文字=public、小文字=private)

3

Ruby: User.new(initialize) → Go: コンストラクタなし、User{}リテラルまたはNewUser()ファクトリ

4

Ruby: include Module → Go: struct埋め込み(似てるがコンパイル時に決定)

メリット

  • 埋め込みで継承の複雑さ(ダイヤモンド問題等)なしにコード再利用可能
  • 大文字小文字だけでアクセス制御 — private/publicキーワード不要

デメリット

  • 動的ディスパッチ(method_missing、respond_to?)がない — メタプログラミング不可
  • コンストラクタがなく初期化検証をファクトリ関数で手動で行う必要がある

ユースケース

RailsのActiveRecordモデル継承構造をGoで再設計する時 Rubyのmodule includeパターンをGoの埋め込みに転換する時