Why use Ruby's attr_accessor, attr_reader and attr_writer? Why use Ruby's attr_accessor, attr_reader and attr_writer? ruby ruby

Why use Ruby's attr_accessor, attr_reader and attr_writer?


You may use the different accessors to communicate your intent to someone reading your code, and make it easier to write classes which will work correctly no matter how their public API is called.

class Person  attr_accessor :age  ...end

Here, I can see that I may both read and write the age.

class Person  attr_reader :age  ...end

Here, I can see that I may only read the age. Imagine that it is set by the constructor of this class and after that remains constant. If there were a mutator (writer) for age and the class were written assuming that age, once set, does not change, then a bug could result from code calling that mutator.

But what is happening behind the scenes?

If you write:

attr_writer :age

That gets translated into:

def age=(value)  @age = valueend

If you write:

attr_reader :age

That gets translated into:

def age  @ageend

If you write:

attr_accessor :age

That gets translated into:

def age=(value)  @age = valueenddef age  @ageend

Knowing that, here's another way to think about it: If you did not have the attr_... helpers, and had to write the accessors yourself, would you write any more accessors than your class needed? For example, if age only needed to be read, would you also write a method allowing it to be written?


All of the answers above are correct; attr_reader and attr_writer are more convenient to write than manually typing the methods they are shorthands for. Apart from that they offer much better performance than writing the method definition yourself. For more info see slide 152 onwards from this talk (PDF) by Aaron Patterson.


It is important to understand that accessors restrict access to variable, but not their content. In ruby, like in some other OO languages, every variable is a pointer to an instance. So if you have an attribute to an Hash, for example, and you set it to be "read only" you always could change its content, but not the content of pointer. Look at this:

irb(main):024:0> class Airb(main):025:1> attr_reader :airb(main):026:1> def initializeirb(main):027:2> @a = {a:1, b:2}irb(main):028:2> endirb(main):029:1> end=> :initializeirb(main):030:0> a = A.new=> #<A:0x007ffc5a10fe88 @a={:a=>1, :b=>2}>irb(main):031:0> a.a=> {:a=>1, :b=>2}irb(main):032:0> a.a.delete(:b)=> 2irb(main):033:0> a.a=> {:a=>1}irb(main):034:0> a.a = {}NoMethodError: undefined method `a=' for #<A:0x007ffc5a10fe88 @a={:a=>1}>        from (irb):34        from /usr/local/bin/irb:11:in `<main>'

As you can see is possible delete a key/value pair from the Hash @a, as add new keys, change values, eccetera. But you can't point to a new object because is a read only instance variable.