210 views
What's the return value of the following Ruby 3.2 code?
require 'net/http' [Net::HTTP, Net::HTTP.class] # => [Net::HTTP, Class] include Net [HTTP, HTTP.class] # => [Net::HTTP, Class] module HTTP end [HTTP, HTTP.class] # => ???
The correct answer is
It raise TypeError
[Net::HTTP, Class]
[HTTP, Module]
[HTTP, Class]
Subscribe to RubyCademy and get free access to all our courses, plus hundreds of fun Ruby cards, quizzes, guides, and tutorials!
In Ruby 3.2, there was a significant change in how the language handles module reopening and redefinition.
If you’ve ever defined a module or class with a name already used in an included module, you might have encountered confusing errors.
In this post, we’ll dive deep into this new behavior, how it differs from earlier versions, and why it was introduced.
Before Ruby 3.2, if a module or class was included, and you tried to define a new module or class with the same name, Ruby would assume you were reopening the existing class or module.
This was especially problematic when working with external libraries. Let’s look at a concrete example using Ruby’s net/http
library.
require 'net/http' include Net # HTTP refers to Net::HTTP because Net is included p HTTP # => Net::HTTP # Let's try to define a new module called HTTP module HTTP end # Ruby 3.1 raises a TypeError here: # => TypeError (HTTP is not a module)
In Ruby 3.1, we get a TypeError
because Net::HTTP
is a class, and Ruby tries to reopen it as a module.
Since classes and modules are different entities, this results in an error. Ruby assumed we were trying to modify Net::HTTP
instead of creating a new HTTP
module.
In Ruby 3.2, this behavior was changed to avoid these cryptic errors. When you define a new module or class with a name that’s already been included from another module (like HTTP
in the example above), Ruby 3.2 creates a new module rather than trying to reopen the existing class or module. This makes it easier to safely define new constants in your code without worrying about accidental conflicts.
Here’s an example showing how Ruby 3.2 behaves:
require 'net/http' [Net::HTTP, Net::HTTP.class] # => [Net::HTTP, Class] include Net [HTTP, HTTP.class] # => [Net::HTTP, Class] module HTTP end [HTTP, HTTP.class] # => [HTTP, Module]
net/http
Library:
require 'net/http'
Net::HTTP
class available. At this point, Net::HTTP
is a class defined in the Net
module.Net
Module:
include Net
include
statement brings all constants from the Net
module into the current namespace, making HTTP
refer to Net::HTTP
.HTTP
in Ruby 3.2:module HTTP end
HTTP
module, separate from Net::HTTP
. The previous behavior of trying to reopen Net::HTTP
no longer occurs.[Net::HTTP, Net::HTTP.class]
returns [Net::HTTP, Class]
because Net::HTTP
is a class.Net
, [HTTP, HTTP.class]
also returns [Net::HTTP, Class]
, since HTTP
refers to Net::HTTP
.HTTP
module, the last expression [HTTP, HTTP.class]
returns [HTTP, Module]
because Ruby 3.2 now treats HTTP
as a new, unrelated module.The reasoning behind this change is that Ruby’s previous behavior could lead to hard-to-debug issues.
When including a module that brings in external constants (like Net::HTTP
), it wasn’t always obvious whether a new module or class definition was reopening an existing constant or creating a new one.
This could lead to cryptic errors like the TypeError
seen in the Ruby 3.1 example.
By changing this behavior in Ruby 3.2, developers can define new classes and modules without worrying about inadvertently modifying included constants.
This ensures safer and more predictable code, especially when working with third-party libraries.
The changes in Ruby 3.2 make it much easier to define new constants without worrying about conflicts with included modules.
No longer will you encounter cryptic TypeError
messages when you accidentally redefine a constant from an included module.
Instead, Ruby 3.2 will simply create a new, unrelated module or class, making your code safer and more modular.
This is a welcome improvement, especially for large applications that include many third-party libraries.
By avoiding unintended reopening of classes or modules, Ruby 3.2 helps you avoid unexpected errors and keeps your code more predictable.
Subscribe to RubyCademy and get free access to all our courses, plus hundreds of fun Ruby cards, quizzes, guides, and tutorials!
RubyCademy ©