String#clone
str.clone -> string String#clone creates a shallow copy of a string. It is functionally identical to String#dup with one important distinction: clone preserves the frozen status of the original string.
That difference matters when you need the copy to behave exactly like the original object. If the source string is frozen, the clone stays frozen too, which can help when you are passing data around and want to keep the same immutability guarantees.
That distinction only matters when the original string has been frozen or carries extra object state that you want to keep on the copy. In everyday code, the method still behaves like a straightforward way to duplicate a string without sharing the same object identity.
Syntax
str.clone
clone accepts no parameters. It returns a new string object. Unlike methods that take arguments to control copy behavior, clone is deliberately simple: you call it on the source and get back an independent copy that shares no internal references with the original.
Basic Usage
original = "hello world"
copy = original.clone
original << "!"
puts original # => "hello world!"
puts copy # => "hello world"
Both clone and dup create shallow copies, so modifying the original does not affect the copy. The two objects live at separate memory addresses and can be changed independently, which is the main reason you reach for either method instead of assigning a second variable to the same object.
Preserving frozen status
The key difference between clone and dup is that clone preserves whether the text object is frozen. When a Ruby string has been frozen—either explicitly with .freeze or implicitly through frozen string literals—the copy’s frozen state depends on which method you choose.
frozen = "immutable".freeze
clone_copy = frozen.clone
dup_copy = frozen.dup
puts frozen.frozen? # => true
puts clone_copy.frozen? # => true
puts dup_copy.frozen? # => false
If you clone a frozen text, the copy stays frozen. If you dup a frozen text, the copy is thawed and can be freely modified. This distinction is the practical reason to prefer one method over the other in most real-world code.
This matters when working with frozen string literals or when you explicitly freeze for performance or immutability. Since Ruby 3.0, the # frozen_string_literal: true magic comment is enabled by default in many projects, making the clone-versus-dup choice more relevant than it was in older codebases.
# Frozen string literal (Ruby 3.0+)
frozen_literal = "already frozen".freeze
puts frozen_literal.clone.frozen? # => true
Shallow copy behavior
A shallow copy duplicates the top-level object but shares any objects it references with the original. For plain text values, this distinction rarely matters because the internal character buffer gets copied too. With custom subclasses that hold extra object state, however, shared references can produce surprising side effects if either the original or the copy later mutates those shared objects.
Like dup, clone creates a shallow copy. The instance variables of a subclass are copied, but objects referenced within those variables are shared:
original = "hello"
copy = original.clone
original.object_id == copy.object_id
# => false - they are different objects
This example confirms that original and copy are different objects with different object_id values, even though they hold the same text content. The copy is independent and safe to modify.
When to use clone vs dup
Since the method is inherited from Object, both clone and dup behave the same on basic text for most purposes. The choice comes down to whether you want to preserve frozen status:
- Use
clonewhen you need to preserve the frozen state of the original. - Use
dupwhen you want a mutable copy regardless of the original’s state.
In practice, most Ruby code uses dup for text because the shallow copy behavior is sufficient and you typically want a mutable working copy. The method you pick is a signal to the next reader about whether immutability matters in that particular code path.
# Cloning a frozen string preserves frozen status
str = "locked".freeze
copy = str.clone
copy.frozen? # => true
# copy << "x" # would raise FrozenError
Errors
TypeError— Strings that are truly unfreezable would raise this, though standard strings do not.