Maybe I just don’t know enough about structs and have been using them blindly but the result below seems irrational to me.
class VarTest < Struct.new(:email)
def perform
puts "Start: #{email}"
if email == "nothing"
email = "bad email"
end
puts "End: #{email}"
end
end
VarTest.new("test@example.com").perform
Unexpected Output:
Start: test@example.com
End:
If I change the code to:
class VarTest < Struct.new(:email)
def perform
e = email
puts "Start: #{e}"
if e == "nothing"
e = "bad email"
end
puts "End: #{e}"
end
end
VarTest.new("test@example.com").perform
We get the expected output:
Start: test@example.com
End: test@example.com
Can someone please explain what is going on with this?
Thanks.
If you replace
email = "bad email"withself.email = "bad email"it will work as expected. This is always true when using setters.The reason is simple: when Ruby encounters a bareword, it tries to resolve it as a local var. If there is none, it will try to call a method by that name. Inside the class body self is the implicit receiver, so readers just work. Now for the writer there’s a problem. If you write something like
foo = "bar", Ruby will create a new local variable, hence you need to make the receiver explicit.This case is a bit trickier:
if email == "nothing"uses the getter. However,email = "bad email"is still seen by the parser and a local variableemailwill be set tonil. This always happens when the parser sees a bareword as the LHS of an assignment. This localnilvalue is what makes it seem like the value ofemaildisappears (which you can verify by only changing the lastputstoputs "End: #{self.email}").