🦆

Interface — Rubyのダックタイピングが型システムに出会った時

明示的なimplementsなし、メソッドが合えば自動的にインターフェースを満たす

Rubyのダックタイピング:「quackメソッドがあればアヒルだ。」ランタイムで確認。なければNoMethodError。

Goのinterface:「quackメソッドがあればQuacker型だ。」コンパイル時に確認。なければコンパイルエラー。

核心の違いはこれだ。同じ哲学だが、Rubyはランタイムで「うまくいけばいい」、Goはコンパイル時に「だめならデプロイできない」。

暗黙的実装 — implementsがない

Javaではclass Dog implements Animalと明示する。Goでは明示しない:

type Quacker interface { Quack() string }

type Duck struct{}
func (d Duck) Quack() string { return "ガー" }
// DuckはQuackerインターフェースを自動的に満たす

DuckがQuackerを実装するという宣言がどこにもない。Quack() stringメソッドがあるから自動的に適格。Rubyのダックタイピング哲学を型システムに持ち込んだもの。

空インターフェース = RubyのObject

interface{}(Go 1.18+ではany)は全ての型が満たすインターフェース。RubyのObjectクラスと同じ。あらゆる値を受けられるが、型安全性を放棄することになる。

io.Reader、io.Writer — 小さなインターフェースの力

Go標準ライブラリはとても小さなインターフェースを基本単位にする。io.ReaderRead(p []byte) (n int, err error)メソッドひとつだけ要求。ファイル、HTTPレスポンス、文字列、圧縮ストリーム — 全てこの一つのインターフェースを満たす。

RubyのIOオブジェクトがreadメソッドを持つのと似ているが、Goではこれが型システムで強制される。

RubyからGoへ

1

Ruby: ダックタイピング(ランタイム確認)→ Go: interface(コンパイル時ダックタイピング)

2

Java: implements明示 → Go: 暗黙的実装(メソッドが合えば自動満足)

3

Ruby: Object(全ての親)→ Go: interface{}/any(全型受容)

4

インターフェースは小さく — io.Readerのようにメソッド1〜2個が理想的

メリット

  • Rubyのダックタイピング哲学を維持しつつコンパイル時安全性まで保証
  • implements宣言不要 — パッケージ間の依存性が緩やかになる

デメリット

  • どのstructがどのinterfaceを満たすかコードだけでは分かりにくい
  • interface{}(any)を乱用するとRubyの動的型付けと変わらなくなる

ユースケース

Rubyのrespond_to?パターンをGoのインターフェース型チェックに転換する時