A class definition behind if false
Question
What's the return value of the following Ruby code?
class MyClass
def self.to_s
"MyClass is cool"
end
end if false
MyClass.to_s # => ???
The correct answer is
-
"MyClass is cool" -
"MyClass" - It raises an error Correct
-
nil
Explanation
TL;DR
A class ... end definition is ordinary runtime code, and the if false modifier applies to the whole definition. It never executes, so the constant MyClass is never created, and MyClass.to_s raises NameError: uninitialized constant MyClass.
Step by step
class MyClass
def self.to_s
"MyClass is cool"
end
end if false
defined?(MyClass) # => nil
begin
MyClass.to_s
rescue NameError => e
e.message # => "uninitialized constant MyClass"
end
class MyClass ... end if falseis one statement: the class definition is the body of a statement modifier, exactly likeputs "hi" if false.- Unlike languages where class declarations are hoisted at compile time, Ruby defines a class only when the
classkeyword executes. It assigns a newClassobject to the constant and runs the body. - The condition is
false, so none of that happens.defined?(MyClass)returnsnil. - Referencing the missing constant raises
NameError. (Had the class existed,MyClass.to_swould have returned"MyClass is cool"since the singleton method overrides the defaultModule#to_s.)
The parse-time contrast
The if false modifier famously does leave a trace when the body is a local-variable assignment: the parser registers the variable even though the assignment never runs. Constants get no such treatment:
x = 1 if false
x # => nil (local variable exists, unassigned)
class Ghost; end if false
defined?(Ghost) # => nil (constant does not exist at all)
Local variables are created at parse time; classes, modules, methods, and constants are created at run time.
A legitimate use
The pattern is real, with defined? as the guard. Ruby's resolv-replace library reopens SOCKSSocket only when the interpreter was compiled with SOCKS support:
class SOCKSSocket < TCPSocket
# patch DNS resolution...
end if defined? SOCKSSocket
When the constant is absent, the whole reopening is skipped and nothing breaks. Use this sparingly: conditionally defined classes make it hard to know what exists at runtime, and a NameError far from the guard is the usual symptom.
Share this quiz
Comments
No comments yet. Be the first.