How to make instance variables private in Ruby? How to make instance variables private in Ruby? ruby ruby

How to make instance variables private in Ruby?


Like most things in Ruby, instance variables aren't truly "private" and can be accessed by anyone with d.instance_variable_get :@x.

Unlike in Java/C++, though, instance variables in Ruby are always private. They are never part of the public API like methods are, since they can only be accessed with that verbose getter. So if there's any sanity in your API, you don't have to worry about someone abusing your instance variables, since they'll be using the methods instead. (Of course, if someone wants to go wild and access private methods or instance variables, there isn’t a way to stop them.)

The only concern is if someone accidentally overwrites an instance variable when they extend your class. That can be avoided by using unlikely names, perhaps calling it @base_x in your example.


Never use instance variables directly. Only ever use accessors. You can define the reader as public and the writer private by:

class Foo  attr_reader :bar  private  attr_writer :barend

However, keep in mind that private and protected do not mean what you think they mean. Public methods can be called against any receiver: named, self, or implicit (x.baz, self.baz, or baz). Protected methods may only be called with a receiver of self or implicitly (self.baz, baz). Private methods may only be called with an implicit receiver (baz).

Long story short, you're approaching the problem from a non-Ruby point of view. Always use accessors instead of instance variables. Use public/protected/private to document your intent, and assume consumers of your API are responsible adults.


It is possible (but inadvisable) to do exactly what you are asking.

There are two different elements of the desired behavior. The first is storing x in a read-only value, and the second is protecting the getter from being altered in subclasses.


Read-only value

It is possible in Ruby to store read-only values at initialization time. To do this, we use the closure behavior of Ruby blocks.

class Foo  def initialize (x)    define_singleton_method(:x) { x }  endend

The initial value of x is now locked up inside the block we used to define the getter #x and can never be accessed except by calling foo.x, and it can never be altered.

foo = Foo.new(2)foo.x  # => 2foo.instance_variable_get(:@x)  # => nil

Note that it is not stored as the instance variable @x, yet it is still available via the getter we created using define_singleton_method.


Protecting the getter

In Ruby, almost any method of any class can be overwritten at runtime. There is a way to prevent this using the method_added hook.

class Foo  def self.method_added (name)    raise(NameError, "cannot change x getter") if name == :x  endendclass Bar < Foo  def x    20  endend# => NameError: cannot change x getter

This is a very heavy-handed method of protecting the getter.

It requires that we add each protected getter to the method_added hook individually, and even then, you will need to add another level of method_added protection to Foo and its subclasses to prevent a coder from overwriting the method_added method itself.

Better to come to terms with the fact that code replacement at runtime is a fact of life when using Ruby.