投稿日:2026/8/11
更新日:2026/8/11

user= はメソッド名)attr_accessor :user を展開すると def user と def user=(value) の2つになる、という説明を読んで
「なぜ = が付くのか」「def user(value) ではダメなのか」で詰まったのでまとめた。
結論から言うと、= はお作法ではなくメソッド名の一部で、これが無いと代入の構文そのものが使えない。
@user は外から直接触れない。だから読み書きの窓口としてメソッドが要るfoo.user = 値 は変数への代入ではなく、user= という名前のメソッドの呼び出しuser と user= は名前の違う別々のメソッド。ゲッターとセッターは「同じメソッドの2つの顔」ではないdef user(value) でも Ruby としては動くが、foo.user = 値 が書けず、ゲッターと共存もできず、Rails の一括代入も壊れるattr_accessor が作るのは変数ではなくメソッド。だからクラス内では self.user = 値 と書く必要があるRuby のインスタンス変数(@ で始まるもの)は、そのオブジェクトの中からしか読み書きできない。
class Foo
def initialize
@user = "たろう"
end
end
foo = Foo.new
foo.@user # ← 文法エラー。そもそもこう書けない
Java の private フィールドと同じで、外部から直接アクセスする手段が言語レベルで存在しない。
外から触りたいなら、メソッドを経由するしかない。
class Foo
def user # ゲッター(読む窓口)
@user # @user の中身を返すだけ
end
def user=(value) # セッター(書く窓口)
@user = value # 渡された値を @user に入れるだけ
end
end
foo = Foo.new
foo.user = "たろう" # セッターが呼ばれる
foo.user # => "たろう" ゲッターが呼ばれる
attr_accessor :user は、この2つのメソッドをその場で定義するだけのショートカット(Module が持つマクロ)。
Rails の機能ではなく素の Ruby の話。
| 書き方 | 定義されるもの |
|---|---|
attr_reader :user |
user(ゲッターだけ) |
attr_writer :user |
user=(セッターだけ) |
attr_accessor :user |
user と user=(両方) |
foo.user = 値 は代入ではないここが最大の勘所。代入しているように見えて、実際は = までを含めた user= というメソッドを呼んでいる。
foo.user = "たろう"
# ↓ Ruby が内部でやっていること(こう書いても同じ)
foo.user=("たろう")
foo.send(:user=, "たろう")
Ruby では = ? ! をメソッド名の末尾に使える。def user=(value) の = はタイポではなく名前の一部。
flowchart TD
W["foo.user = 値 と書いた"] --> W2["user= という名前のメソッドを探す"]
W2 --> W3["見つかったら実行して @user に代入"]
R["foo.user と書いた"] --> R2["user という名前のメソッドを探す"]
R2 --> R3["見つかったら実行して @user を返す"]
つまり user と user= は、Ruby から見ると foo と foo! くらい別物の名前。
| 書き方 | 呼ばれるメソッド名 | 何をするか |
|---|---|---|
foo.user |
user |
@user を返す(読む) |
puts foo.user |
user |
同上。右辺が無ければ常にゲッター |
foo.user = "たろう" |
user= |
@user に入れる(書く) |
見分け方は単純で、foo.user の後ろに = 値 が続いているかどうかだけ。
どちらが呼ばれたか自信が無いときは、
def user; p "ゲッター"; @user; endのようにpを仕込むと一発で分かる。
def user(value) ではなぜダメか引数を1つ取る普通のメソッドとして定義すること自体は、Ruby の文法上まったく問題ない。
class Foo
def user(value)
@user = value
end
end
foo.user("たろう") # => 動く。ただのメソッド呼び出し
動くのに使わないのは、次の3つを失うから。
= を使った代入が書けないfoo.user = "たろう"
# => NoMethodError: undefined method `user=' for an instance of Foo
Ruby は .user = という構文を見たら、必ず user= という名前のメソッドを探しに行く。user という名前のメソッドで代用してはくれない。
Ruby にはオーバーロード(同名で引数違いのメソッド)が無い。後から定義した方が前を上書きする。
class Foo
def user # 読む用
@user
end
def user(value) # ← 上書きしてしまう。読む用が消える
@user = value
end
end
foo.user # => ArgumentError: wrong number of arguments (given 0, expected 1)
user= という別名にすることで、初めて読み書きの2つを同時に持てる。
実務でいちばん効くのはこれ。Rails は属性を代入するとき、内部でほぼ必ず send("#{キー名}=", 値) を呼んでいる。
sequenceDiagram
participant C as コントローラ
participant F as SignupForm
participant S as セッター email=
C->>F: SignupForm.new(email: "x")
F->>S: send("email=", "x")
S->>S: @email に "x" を代入
SignupForm.new(email: "x", company_name: "y")
form.assign_attributes(email: "z")
form.update(params)
これらは全部 email= company_name= を探して呼び出す。def email(value) と定義していると、どれも NoMethodError になる。
フォームオブジェクトで attr_accessor を書くのは、この規約に乗るため。
self. が要るattr_accessor が作るのはメソッドであって変数ではない。@user は、セッターが初めて呼ばれたときに生まれる。
そのためクラスの内部で self. を省くと、セッターではなくローカル変数への代入になる。
class Foo
attr_accessor :user
def do_something
user = "たろう" # ← バグ。ローカル変数を作っただけでセッターは呼ばれていない
self.user = "たろう" # ← 正解。self を付けて初めてセッターが呼ばれる
end
end
読むときは user だけで済む(self.user でもよい)のに、書くときだけ self. が必須になるのが非対称でハマりやすい。
セッターだけの特殊ルールとして、中で何を返しても代入式全体の値は右辺の値になる。
class Foo
def user=(value)
@user = value
"何か別の値" # 何を返しても……
end
end
result = (foo.user = "たろう")
result # => "たろう" ← 右辺がそのまま返る
「代入式の値は代入した値」という直感を壊さないための仕様。
セッターの戻り値を工夫しても呼び出し側は受け取れないので、return で何かを返す設計にしない。