Ruby Quiz

157 views

What’s the return value of this Ruby 3 code?

obj = "x"
def obj.tag = :singleton

a = obj.dup
b = obj.clone

[a.respond_to?(:tag), b.respond_to?(:tag)] # => ???

The correct answer is

[true, false]

[false, true]

[true, true]

[false, false]

Unlock Your Ruby Potential

Subscribe to RubyCademy and get free access to all our courses, plus hundreds of fun Ruby cards, quizzes, guides, and tutorials!

Explanation

We begin with a simple string instance and attach a singleton method to it. Only that instance should have the method.

obj = "x"

This creates a fresh String whose content is the character x. The object has its own identity and no custom behavior yet.

def obj.tag = :singleton

This opens the eigenclass of obj and defines a method named tag that returns the symbol :singleton. The method belongs to obj only. Other strings are not affected because String itself has not changed.

obj.tag

This returns :singleton. The call proves that the singleton method exists on this instance.

Copying with dup

Now we copy obj with dup and observe what was carried over.

a = obj.dup

This creates a shallow copy with the same class and the same content. The new object has a different identity. The eigenclass is not copied. The singleton method is not present on a.

a.respond_to?(:tag)

This returns false. The copy created by dup does not have the tag method.

a == obj

This returns true. The two strings have equal content even though they are different objects.

a.equal?(obj)

This returns false. equal? checks identity. The objects are distinct.

Copying with clone

Now we copy obj with clone and compare the behavior.

b = obj.clone

This creates a shallow copy that preserves the eigenclass. The singleton method tag is copied onto b because the eigenclass relationship is preserved.

b.respond_to?(:tag)

This returns true. The copy created by clone responds to the singleton method.

b.tag

This returns :singleton. The method behaves the same as on obj.

Verifying singleton methods directly

It is useful to look at the list of singleton methods for each object.

obj.singleton_methods.sort

This returns [:tag]. The original object exposes the singleton method.

a.singleton_methods.sort

This returns []. The copy from dup has no singleton methods.

b.singleton_methods.sort

This returns [:tag]. The copy from clone preserved the singleton method.

Putting it all together

The simplest mental model is the following. dup makes a content level copy of the object. It keeps instance variables and basic state. It does not carry over the meta level where singleton methods and per object module extensions live. clone makes a closer copy. It preserves the meta level, the frozen state, and any taint or trust flags on older Rubies. For modern Ruby, you mostly care about singleton methods, extended modules, and frozen state. When in doubt, reach for dup if you want a clean copy without ad hoc behavior. Reach for clone if you need to keep exactly what the original could do.

Final check

We reproduce the minimal example and explain the outcome in one line per call.

obj = "x"

This creates the original string.

def obj.tag = :singleton

This adds a singleton method to the instance.

a = obj.dup

This makes a copy without the eigenclass. a does not have tag.

b = obj.clone

This makes a copy that preserves the eigenclass. b has tag.

[a.respond_to?(:tag), b.respond_to?(:tag)]

This produces [false, true]. The first element is false because dup strips singleton behavior. The second element is true because clone preserves it.

Voilà!

Unlock Your Ruby Potential

Subscribe to RubyCademy and get free access to all our courses, plus hundreds of fun Ruby cards, quizzes, guides, and tutorials!

RubyCademy ©