545

Ruby has this handy and convenient way to share instance variables by using keys like

attr_accessor :var
attr_reader :var
attr_writer :var

Why would I choose attr_reader or attr_writer if I could simply use attr_accessor? Is there something like performance (which I doubt)? I guess there is a reason, otherwise they wouldn't have made such keys.

mfaani
  • 33,269
  • 19
  • 164
  • 293
Saturn
  • 17,888
  • 49
  • 145
  • 271
  • 2
    possible duplicate of [What is attr\_accessor in Ruby?](http://stackoverflow.com/questions/4370960/what-is-attr-accessor-in-ruby) – sschuberth Dec 11 '14 at 16:53

5 Answers5

776

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 = value
end

If you write:

attr_reader :age

That gets translated into:

def age
  @age
end

If you write:

attr_accessor :age

That gets translated into:

def age=(value)
  @age = value
end

def age
  @age
end

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?

Wayne Conrad
  • 103,207
  • 26
  • 155
  • 191
  • 59
    There is also a *significant* performance advantage to writing `attr_reader :a` vs. `def a; return a; end` http://confreaks.net/videos/427-rubyconf2010-zomg-why-is-this-code-so-slow – Nitrodist Jan 19 '12 at 19:22
  • 85
    @Nitrodist, Interesting. For Ruby 1.8.7, the `attr_reader` defined accesor takes 86% of the time that the manually defined accessor does. For Ruby 1.9.0, the `attr_reader` defined accessor takes 94% of the time that the manually defined accessor does. In all of my tests, however, accessors are fast: an accessor takes about 820 nanoseconds (Ruby 1.8.7) or 440 nanoseconds (Ruby 1.9). At those speeds, you'll need to call an accessor hundreds of millions of times for the performance benefit of `attr_accessor` to improve overall runtime by even one second. – Wayne Conrad Jan 19 '12 at 19:48
  • 24
    "Presumably, it is set by the constructor of this class and remains constant." That is not accurate. Instance variables with readers might change frequently. However it is intended that their values only be changed privately by the class. – mlibby Aug 25 '12 at 13:19
  • @mcl, Good point. I favor functional idioms, which flavored the way I worded that paragraph. I've (hopefully) improved it. Thanks! – Wayne Conrad Aug 25 '12 at 13:48
  • 12
    You can use "," to add more than 2 attributes, such as: `attr_accessor :a, :b` – Andrew_1510 Jan 22 '14 at 06:55
  • @WayneConrad The time difference is because attr_accessor use class_eval( code ) which runs code on fly rather than creating method. – Deepender Singla Jul 06 '14 at 04:44
  • Okay that's the point, but what could be a use case ? Things I don't get… Apart showing how you'd handle the attributes ? – Ben Oct 24 '14 at 15:15
  • @Ben I'd like to improve the answer. Can you please help me to understand what doesn't make sense? – Wayne Conrad Oct 24 '14 at 16:58
  • @WayneConrad finally got it; perfect answer. it was a bit too abstract for me at first. – Ben Oct 25 '14 at 21:17
  • 3
    for what is worth after all these years: https://github.com/JuanitoFatas/fast-ruby#attr_accessor-vs-getter-and-setter-code according to the latest benchmarks on ruby 2.2.0 attr_* are faster than getters and setters. – molli Nov 14 '17 at 17:50
28

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.

Benjamin Oakes
  • 12,262
  • 12
  • 65
  • 83
hawx
  • 1,313
  • 12
  • 10
18

It is important to understand that accessors restrict access to variables, 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:

> class A
>   attr_reader :a
>   def initialize
>     @a = {a:1, b:2}
>   end
> end
=> :initialize
> a = A.new
=> #<A:0x007ffc5a10fe88 @a={:a=>1, :b=>2}>
> a.a
=> {:a=>1, :b=>2}
> a.a.delete(:b)
=> 2
> a.a
=> {:a=>1}
> 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.

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Korsmakolnikov
  • 709
  • 7
  • 15
17

Not all attributes of an object are meant to be directly set from outside the class. Having writers for all your instance variables is generally a sign of weak encapsulation and a warning that you're introducing too much coupling between your classes.

As a practical example: I wrote a design program where you put items inside containers. The item had attr_reader :container, but it didn't make sense to offer a writer, since the only time the item's container should change is when it's placed in a new one, which also requires positioning information.

Chuck
  • 234,037
  • 30
  • 302
  • 389
14

You don't always want your instance variables to be fully accessible from outside of the class. There are plenty of cases where allowing read access to an instance variable makes sense, but writing to it might not (e.g. a model that retrieves data from a read-only source). There are cases where you want the opposite, but I can't think of any that aren't contrived off the top of my head.

coreyward
  • 77,547
  • 20
  • 137
  • 166