Ruby

rbs-inlineとは?Rubyの型をコードに書く記法とRBS 4系への移行判断

rbs-inlineは、Rubyのソースコードにコメントの形で型を書き込む記法です。型情報を実装と同じファイルに置けるため、別立ての.rbsを手で書き続ける必要がなくなります。ただし2026年8月時点では、同名のgemと、RBS本体に取り込まれた組み込みのインラインRBSという2つの実装が並存しています。どちらを前提にするかで有効化の方法もツール構成も変わるため、記事や社内ドキュメントの記述が食い違う原因になっています。この記事では、rbs-inlineのREADME、RBS 4.1.1のCHANGELOGとdocs/inline.md、Steep 2.0のCHANGELOGと実ソースを一次情報として、記法・導入手順・選び方を整理します。

まとめ

  • rbs-inline gemは、注釈付きRubyコードからRBSファイルを生成するトランスパイラです。READMEに「テスト用のプロトタイプ」「このリポジトリは活発に更新していない」と明記され、rbs gem本体への統合後に廃止する予定が公表されています。
  • RBS 4.0(2026年3月16日)がインラインRBS構文を実験的機能として取り込み、Steep 2.0(2026年4月14日)がSteepfileのcheck(inline: true)指定でRubyファイルを直接型検査できるようになりました。RBSファイルの生成ステップは不要です。
  • Ruby 4.0.0が同梱するrbsは3.10.0です。組み込みのインラインRBSを使うには、Gemfileでrbs 4系を明示的に指定します。
  • 新規に始めるならRBS 4系とSteep 2.0の組み合わせが本命です。gemの利用者へsigディレクトリを配布したい場合だけ、生成コマンドを持つrbs-inline gemを選びます。

以下では、2つの実装の関係、RBS 4.1.1のdocs/inline.mdが定める注釈構文、それぞれの導入手順、そして注釈が無視される具体的な条件までを順に見ていきます。

rbs-inlineの現在地|プロトタイプgemからRBS本体への移管

rbs-inline gemの役割と開発状況

rbs-inline gemは、RBSとSteepのメンテナであるSoutaro Matsumoto氏が2024年4月にリポジトリを公開し、同年5月2日に初版0.1.0をリリースしたツールです。Rubyコード中の@rbsコメントを読み取り、対応する.rbsファイルを書き出すトランスパイラとして動作します。生成された型定義はSteepをはじめRBSを扱うツールがそのまま利用できます。

ただしREADMEの冒頭には、作者がインラインRBS構文をrbs gem本体へ実装中であること、このリポジトリは活発に更新していないこと、そして本体へ統合した後にrbs-inline gemを非推奨とする計画が明記されています。最新版は0.14.0(2026年5月10日公開)で、対応Rubyは3.1以上、ライセンスはMITです。RubyGemsでの累計ダウンロードは519,636件(2026年8月3日時点)と実運用の蓄積はありますが、新規プロジェクトの土台として長期に見込める位置づけではありません。

RBS 4.0が取り込んだインラインRBSとSteep 2.0の対応

RBS 4.0(2026年3月16日)のリリースノートは、実験的機能としてインラインRBS構文のサポートを同梱したと述べています。続く4.1.0(2026年7月27日)ではdef self.による特異メソッド定義、module-self制約、module宣言内のインスタンス変数注釈が追加されました。いずれのリリースノートも「インラインRBSは依然として実験的であり、将来のリリースで変更される可能性がある」と注記しています。最新は4.1.1(2026年7月30日)です。

型検査側はSteep 2.0(2026年4月14日)が対応しました。Steepfileでcheck(inline: true)を指定したファイルは、Rubyコード中の型宣言をそのまま読み取って型検査されます。メソッド型・attr_*・インスタンス変数・定数・mixin・継承に対応し、補完、ホバー、定義ジャンプといったエディタ連携も同時に提供されます。

注意したいのは、rbs 4.1.1のCLIにはinlineコマンドが存在しない点です。実装されているのはastprototypeなどの既存サブコマンドで、インライン注釈から.rbsを書き出す機能は本体に含まれていません。本体側は「パーサを提供し、Steepなどの利用側が直接読む」という設計で、gem版の「生成する」という設計とは役割が違います。

インラインRBSの書き方|メソッド型・doc形式・宣言まわりの注釈

以下の構文はRBS 4.1.1のdocs/inline.mdに基づきます。gem版もほぼ同じ記法ですが、細部は同gemのWikiにあるSyntax guideを確認してください。

メソッド型を書く@rbsと#:|オーバーロードの違い

基本になるのは、メソッド型をRBSファイルと同じ記法で1行書く形です。docs/inline.mdも最初の例にこの形を挙げています。@rbsに続けて書く方法と、#:で書く方法があります。

class Calculator
  # @rbs (Integer, Integer) -> Integer
  def add(x, y) = x + y

  #: (Integer, Integer) -> Integer
  def subtract(x, y) = x - y

  # 戻り値だけを末尾に書くこともできます
  def double(x) #: Integer
    x * 2
  end
end

オーバーロードの書き方は2つの構文で異なります。@rbsはRBSファイルと同じく|演算子で複数の型をつなぎますが、#:は行を分けて複数回書きます。混在させると読み手が構文を取り違えるため、プロジェクト単位でどちらかに寄せるのが無難です。

引数ごとに型を書くdoc形式

引数の多いメソッドでは、パラメータごとに型と説明を並べるdoc形式が読みやすくなります。--の後ろに説明文を書けます。

class Calculator
  # @rbs x: Integer -- 必須の位置引数
  # @rbs y: Integer -- 省略可能な位置引数
  # @rbs a: String -- 必須のキーワード引数
  # @rbs return: Integer
  def add(x, y = 1, a:)
    x + y
  end

  # @rbs *a: String -- a の型は Array[String] になります
  # @rbs &block: () -> void
  def each(*a, &block)
  end
end

スプラット引数は要素の型を書けばArray[その型]として扱われ、ダブルスプラットはHash[Symbol, その型]として扱われます。上のサンプルの*a: StringArray[String]**b: boolと書けばHash[Symbol, bool]です。ブロック引数は?を前置すると省略可能なブロックになります。

属性・インスタンス変数・定数・mixinの宣言

メソッド以外の宣言も同じ仕組みで書けます。attr_readerなどの属性は末尾の#:で型を与え、インスタンス変数はclassまたはmoduleの直下に@rbs @名前: 型を置きます。

class Person
  # @rbs @name: String
  # @rbs @age: Integer?

  attr_reader :name #: String
  attr_accessor :email #: String?

  # 1行に複数書いた場合、型は全ての属性に適用されます
  attr_reader :first_name, :last_name #: String

  include Comparable
  extend Enumerable #[String]
end

MAX_RETRIES = 3
CONFIG = { name: "app", version: 1 } #: { name: String, version: Integer }

定数は右辺がリテラルであれば型が推論されます。整数はInteger、真偽値はbool、シンボルはSymbolという具合です。推論に任せられない複雑な値だけ#:で明示します。型定義に取り込みたくない宣言には@rbs skipを付けると、その直後の定義が無視されます。

導入手順|gem版とRBS 4系+Steep 2.0版

gem版はマジックコメントとsig生成が前提

gem版は型定義への依存を実行時に持ち込まないよう、require: false付きで追加します。対象ファイルにはマジックコメントが必要で、これが無いファイルは解析されません。

# Gemfile
gem 'rbs-inline', require: false

# 対象のRubyファイル先頭
# rbs_inline: enabled

# 生成結果を標準出力へ確認
$ bundle exec rbs-inline lib

# sig/generated 以下へ書き出し
$ bundle exec rbs-inline --output lib

書き出される.rbsは、注釈から組み立てた通常の型定義です。READMEに載っている例では、左の注釈付きRubyが右のRBSに変換されます。

# 注釈付きのRuby
class Person
  attr_reader :name #: String

  # @rbs &block: (String) -> void
  def each_address(&block) #: void
    addresses.each(&block)
  end
end

# 生成されるRBS
class Person
  attr_reader name: String

  def each_address: () { (String) -> void } -> void
end

マジックコメントを1ファイルずつ足すのが現実的でない場合は、--opt-outを付けると全ファイルが対象になり、# rbs_inline: disabledを書いたファイルだけが除外されます。既存コードへ一括適用するならこちらが向きます。

編集のたびに手動で回すのは現実的ではないため、READMEではfswatchなどのファイル監視ツールと組み合わせる方法が案内されています。また#:構文はSteep 1.8.0.dev未満と衝突するため、gem版を使うならSteepも合わせて更新します。

もう一点、RuboCopを併用しているプロジェクトでは既定設定のままだと注釈が指摘対象になります。Layout/LeadingCommentSpaceLayout/LineLengthはどちらもAllowRBSInlineAnnotationを既定でfalseにしているため、#:で始まるコメントは「コメントは空白で始めること」に引っかかり、長い型注釈は行長超過として数えられます。.rubocop.ymlで両copのAllowRBSInlineAnnotationtrueにしてから注釈を書き始めると、後追いの一括修正を避けられます。

RBS 4系+Steep 2.0はSteepfileへの1行追加のみ

組み込み版はマジックコメントも生成コマンドも使いません。GemfileでRBSとSteepのバージョンを指定し、Steepfileで対象パターンにinline: trueを付けるだけです。

# Gemfile
gem 'rbs', '~> 4.1', require: false
gem 'steep', '~> 2.0', require: false

# Steepfile
target :lib do
  signature "sig"
  check "lib", inline: true
  ignore "lib/generated", inline: true
end

$ bundle exec steep check

Steepの実ソース(steep 2.0.0のlib/steep/project/dsl.rb)でも、checkignoreの両方がinline:キーワードを受け取り、通常のソースとは別のパターン集合として扱われることを確認できます。既存のsigディレクトリはそのまま併用できるため、新しく書くファイルからインラインへ寄せるといった段階的な移行が可能です。

ここで見落としやすいのがRubyの同梱バージョンです。Ruby 4.0.0のbundled_gemsが同梱するrbsは3.10.0で、インラインRBSが入る前の系列にあたります。Rubyを4系へ上げただけでは組み込みのインラインRBSは使えないため、Gemfileでの明示指定が必須になります。Ruby本体のバージョン差分についてはRuby 4.0.1がリリースされました – 新機能満載のRuby 4系に初の安定版パッチが登場しましたもあわせて確認してください。

2026年時点の選び方|アプリの型検査は組み込み版・sig配布はgem版

結論から言えば、アプリケーションのコードを自社で型検査する目的なら、rbs-inline gemを新規採用する理由はもうありません。生成した.rbsとRubyコードの二重管理が消え、監視プロセスも不要になる組み込み版のほうが構成が単純だからです。gem版を選ぶ意味が残るのは、公開gemの利用者へsigを同梱して配布したいときのように、成果物として.rbsファイルそのものが要るケースに限られます。本体側には生成コマンドが無いためです。

比較軸 rbs-inline gem RBS 4系 + Steep 2.0
最新版 0.14.0(2026-05-10) rbs 4.1.1 / steep 2.0.0
開発状況 活発な更新なし・将来廃止予定 実験的機能として開発継続
有効化 マジックコメント Steepfileの inline: true
.rbs生成 あり なし(直接型検査)
必要Ruby 3.1以上 3.2以上

どちらを選んでも、インラインRBS自体が実験的機能である点は変わりません。RBS 4.1.0でも構文の追加が続いており、リリースノートは将来の変更可能性を明記しています。型注釈をコードに直接埋め込む以上、構文が変われば影響範囲はソース全体に及びます。全ファイルを一度に移行するより、変更頻度の高いディレクトリから試し、既存の.rbsを残しておくほうが安全です。特にRailsのFat Model対策|1500行model.rbを分割する4手法と判断基準で扱ったような肥大化したクラスは、注釈を入れる前に分割したほうが型の恩恵を受けやすくなります。

型が付かない・注釈が無視される条件

インラインRBSは、書いたつもりの注釈が黙って無視されることがあります。RBS 4.1.1のドキュメントが明示している制約は次のとおりです。

  • 型注釈の無いメソッドは、スーパーメソッドが無ければ(?) -> untypedとして扱われます。引数の型検査は行われません。スーパークラスやincludeしたモジュールに同名メソッドがあれば、その型を引き継ぎます。
  • ジェネリックなクラス定義とモジュール定義には対応していません。
  • メソッドと属性の可視性指定に未対応で、属性はすべてpublicになります。
  • class << self構文、クラスやモジュールの外側で定義したトップレベルのメソッドと属性、def obj.fooのようなself以外のレシーバを持つ定義は取り込まれません。
  • クラス名とモジュール名は定数のみが対象です。Class.newによる動的定義やローカル変数経由のincludeは無視されます。
  • mixinの引数は1つだけで、include A, Bという書き方には対応していません。
  • インスタンス変数の宣言はclassとmoduleの直下に限られ、メソッド定義の中に書いたものは無視されます。
  • 属性名はシンボルリテラルである必要があります。文字列やローカル変数で渡すと定義が無視されます。

特に1つ目は、注釈を書き忘れたメソッドがエラーにならず素通りするという意味です。移行の初期段階では、型が付いていない箇所を検知する仕組みをCIに入れておかないと、型検査が通っているのに実質untypedというディレクトリができあがります。なおclass << selfへの対応はruby/rbsでプルリクエストが提出済みですが、2026年8月3日時点では未マージです。

よくある質問

rbs-inlineとRBSは何が違いますか

RBSはRubyの型を記述する言語仕様と、それを扱うgemの名前です。rbs-inlineは、そのRBSの記述を.rbsファイルではなくRubyコードのコメントに書くための仕組みを指します。現在は名称としてrbs-inline gemと、RBS本体のインラインRBS機能の両方を指す言葉になっています。

rbs-inline gemはもう使うべきではありませんか

既存プロジェクトで動いているなら、急いで外す必要はありません。ただしREADMEが廃止予定を明言しているため、新規採用は避け、.rbsの配布が不要な用途から順に組み込み版へ寄せるのが妥当です。

Ruby 4.0を入れればインラインRBSがそのまま使えますか

使えません。Ruby 4.0.0が同梱するrbsは3.10.0で、インラインRBSはRBS 4.0で入った機能です。Gemfileでrbs 4系を指定してください。

型注釈を書いていないメソッドはどう扱われますか

スーパーメソッドが無い場合は(?) -> untypedになり、引数と戻り値の検査が行われません。エラーにはならないため、未注釈の箇所は自分で洗い出す必要があります。

既存のsigディレクトリと併用できますか

併用できます。Steepfileではsignatureで従来の.rbsを読み込みつつ、checkinline: trueを付けたパターンだけをインライン対象にできます。ファイル単位で移行を進められます。

関連記事

資料請求

RELATED POSTS 関連記事