What's the point of instance_variable_set
? Aren't these two lines the same?
instance_variable_set(@name, value)
@name = value"
What's the point of instance_variable_set
? Aren't these two lines the same?
instance_variable_set(@name, value)
@name = value"
In the case of a "simple" variable assignment for an instance variable like:
@foo = "foo"
You couldn't do
"@#{foo}" = "bar" # syntax error, unexpected '=', expecting end-of-input
But you could do something similar with instance_variable_set:
instance_variable_set("@#{foo}", "bar")
p @foo # "bar"
As per your question Aren't these two lines the same?, for that example they're similar, but isn't the use people tend to give to instance_variable_set.
I wonder why there is no mention of another obvious difference: they have different scopes.
While @name = value
is accessible only from within the scope where the instance variable is defined (read: from inside the instance,) instance_variable_set
is available from everywhere to set instance variables from outside:
class C
attr_reader :name
def initialize(name)
@name = name
end
end
C.new("foo").tap do |c|
c.instance_variable_set(:@name, 42)
c.name
end
#⇒ 42
I'm pretty new to Ruby and have this question when I'm reading some tutorial. I'm curious what's the point of
instance_variable_set
?
The point of Object#instance_variable_set
is to dynamically reflectively set an instance variable whose name may not be known at design time, only at run time.
Aren't these two lines the same?
instance_variable_set(@name, value) @name = value
No, these lines are completely different, and they perfectly illustrate what I wrote above:
@name
to value
.@name
to value
.From the fine manual:
instance_variable_set(symbol, obj) → obj
instance_variable_set(string, obj) → objSets the instance variable named by
symbol
to the given object, thereby frustrating the efforts of the class's author to attempt to provide proper encapsulation. The variable does not have to exist prior to this call. If the instance variable name is passed as a string, that string is converted to a symbol.
So the first argument isn't @name
, it is :@name
(i.e. a Symbol) or '@name'
(a String).
The result is that instance_variable_set
, as noted in the documentation, can be used to set an instance variable when you know its name even if you don't know the name until your code is running.
Here is an example of how the methods Object#instance_variable_set and Object#instance_variable_get could be used to increment the values of all instance variables by one.
class Klass
attr_accessor :a, :b, :cat
def initialize
@a, @b, @c, @d = 1, 2, 3, 4
end
end
k = Klass.new
#=> #<Klass:0x0000000001d70978 @a=1, @b=2, @c=3, @cat=4>
k.instance_variables.each { |v| k.instance_variable_set(v, k.instance_variable_get(v)+1) }
#=> [:@a, :@b, :@c, :@cat]
k #=> #<Klass:0x0000000001d70978 @a=2, @b=3, @c=4, @cat=5>
See also Object#instance_variables.
Compared to having four separate assignment statements, fewer lines of code are needed, but there are two other, more important advantages:
k.cut += 1
); andA variant of this is to substitute a dynamically-constructed array of instance variable names (e.g., [:@a, :@b]
) for instance_variables
above.
These may seem like unusual examples, but they are representative of a large class of operations involving instance variables in which this kind of batch processing can be used to advantage.