Freezing an Object to Prevent Changes

Problem

You want to prevent any further changes to the state of an object.

Solution

Freeze the object with Object#freeze:

	frozen_string = 'Brrrr!'
	frozen_string.freeze
	frozen_string.gsub('r', 'a') # => "Baaaa!"
	frozen_string.gsub!('r', 'a')
	# TypeError: can't modify frozen string

 

Discussion

When an object is frozen, its instance variables are permanently bound to their current values. The values themselves are not frozen: their instance variables can still be modified, to the extent they were modifiable before:

	sequences = [[1,2,3], [1,2,4], [1,4,9]].freeze
	sequences << [2,3,5]
	# TypeError: can't modify frozen array
	sequences[2] << 16 # => [1, 4, 9, 16]

A frozen object cannot be unfrozen, and if cloned, the clone will also be frozen. Calling Object#dup (as opposed to Object#clone) on a frozen object yields an unfrozen object with the same instance variables.

	frozen_string.clone.frozen? # => true
	frozen_string.dup.frozen? # => false

Freezing an object does not prevent reassignment of any variables bound to that object.

	frozen_string = 'A new string.'
	frozen_string.frozen? # => false

To prevent objects from changing in ways confusing to the user or to the Ruby interpreter, Ruby sometimes copies objects and freezes the copies. When you use a string as a hash key, Ruby actually copies the string, freezes the copy, and uses the copy as the hash key: that way, if the original string changes later on, the hash key isn't affected.

Constant objects are often frozen as a second line of defense against the object being modified in place. You can freeze an object whenever you need a permanent reference to an object; this is most commonly seen with strings:

	API_KEY = "100f7vo4gg".freeze

	API_KEY[0] = 4
	# TypeError: can't modify frozen string

	API_KEY = "400f7vo4gg"
	# warning: already initialized constant API_KEY

Frozen objects are also useful in multithreaded code. For instance, Ruby's internal file operations work from a frozen copy of a filename instead of using the filename directly. If another thread modifies the original filename in the middle of an operation that's supposed to be atomic, there's no problem: Ruby wasn't relying on the original filename anyway. You can adopt this copy-and-freeze pattern in multithreaded code to prevent a data structure you're working on from being changed by another thread.

Another common programmer-level use of this feature is to freeze a class in order to prevent future modifications to it (by yourself, other code running in the same environment, or other people who use your code as a library). This is not quite the same as the final construct in C# and Java, because you can still subclass a frozen class, and override methods in the subclass. Calling freeze only stops the in-place modification of a class. The simplest way to do it is to call freeze as the last statement in the class definition:

	class MyClass
	 def my_method
	 puts "This is the only method allowed in MyClass."
	 end
	 freeze
	end

	class MyClass
	 def my_method
	 "I like this implementation of my_method better."
	 end
	end	
	# TypeError: can't modify frozen class

	class MyClass
	 def my_other_method
	 "Oops, I forgot to implement this method."
	 end
	end
	# TypeError: can't modify frozen class

	class MySubclass < MyClass
	 def my_method
	 "This is only one of the methods available in MySubclass."
	 end

	 def my_other_method
	 "This is the other one."
	 end
	end

	MySubclass.new.my_method
	# => "This is only one of the methods available in MySubclass."

 

See Also

  • Recipe 4.7, "Making Sure a Sorted Array Stays Sorted," defines a convenience method for making a frozen copy of an object
  • Recipe 5.5, "Using an Array or Other Modifiable Object as a Hash Key"
  • Recipe 8.16, "Making a Copy of an Object"
  • Recipe 8.17, "Declaring Constants"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net