Ruby DISCUSSION

Ruby attr_accessor: why does assigning inside a method not update my object's attribute?

Started by anant Ruby attr_accessorinstance variablesself setterlocal variable shadowinggetter and setter
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Ruby attr_accessor: why does assigning inside a method not update my object's attribute?

anant Ruby Forum
#1

My class declares attr_accessor :speed. In a method I wrote speed = 0 to reset it, but afterwards motor.speed still returns the old value. Reading speed in the same method works fine, and @speed = 0 works too. In another class I mistyped @sped and got nil instead of an error.

What does attr_accessor actually generate, and what are the rules for using the attribute from inside the class?

Community replies 5

Re: Ruby attr_accessor: why does assigning inside a method not update my object's attribute?

#2

attr_accessor :speed is a method call that runs while the class is being defined and generates two ordinary methods: a reader, def speed; @speed; end, and a writer, def speed=(value); @speed = value; end. attr_reader generates only the first and attr_writer only the second.

The data itself lives in the instance variable @speed, which belongs to each object and cannot be reached from outside except through methods. motor.speed = 5 is a call to the method named speed=.

Re: Ruby attr_accessor: why does assigning inside a method not update my object's attribute?

#3

Inside a method, speed = 0 is parsed as assignment to a new local variable called speed. Ruby decides this at parse time: a bare name on the left of = is always a local. The writer method is never called, and the local disappears when the method returns. To call the writer you must give an explicit receiver: self.speed = 0.

Reading is different. A bare speed with no local of that name in scope is a method call on self, so the reader works without self. That asymmetry is what makes this bug so common.

Re: Ruby attr_accessor: why does assigning inside a method not update my object's attribute?

#4

Whether to write self.speed = 0 or @speed = 0 inside the class is a design choice. Going through the writer means any logic in it always runs, which matters once you replace the generated method with your own: def speed=(v); raise ArgumentError, "0..255" unless v.between?(0, 255); @speed = v; end. Assigning @speed directly bypasses that check.

A common convention is to touch instance variables directly only in initialize and inside the accessors themselves.

Re: Ruby attr_accessor: why does assigning inside a method not update my object's attribute?

#5

The @sped typo shows another property of instance variables: they are not declared, and reading one that was never assigned gives nil without an error. The mistake shows up later as a NoMethodError on nil, away from the typo. Using the reader method instead is safer, because a misspelt method name such as sped raises NameError immediately at the point of the mistake.

Running the script with warnings enabled, ruby -w, also helps catch suspicious code such as unused local variables, which is what the accidental speed = 0 creates.

Re: Ruby attr_accessor: why does assigning inside a method not update my object's attribute?

#6

Expose only what callers need. If outside code should read the speed but not set it, use attr_reader :speed and keep the writer private or absent; every public writer is a way for other code to put the object into a state you did not plan for.

For simple value holders with no behaviour, Struct saves the boilerplate: Sample = Struct.new(:time, :value) gives a class with a constructor, accessors for both fields and value-based equality, so Sample.new(1, 3.3) == Sample.new(1, 3.3) is true, which is not the case for a plain class unless you define == yourself.

TEP COMMUNITY