Hashing Arrays in Ruby vs. C#

I’m in the middle of Ruby Under a Microscope by Pat Shaughnessy. I love the book so far, and I will certainly post a more specific review and recommendation soon, but I just noticed a real semantic difference between Ruby and C# that I wanted to blog about, because it would certainly have tripped me up if I had stumbled across it in production code. From Shaughnessy’s chapter on hash tables:

In the case of strings and arrays, Ruby actually iterates through all the characters in the string or the elements in the array and calculates a cumulative hash value (p. 183)

“That’s weird,” I thought, “I bet that means that it’s not safe to use arrays as hash table keys in Ruby. So I did an experiment:

def does_it_change
mutable_array = [1,2]
hash = {}
hash[mutable_array] = 'foo'
puts hash[mutable_array].inspect # prints "foo"
mutable_array[1] = 3
puts hash[mutable_array].inspect # prints "nil"

Sure enough, if you use an array as a hash key in Ruby, and then mutate the array, and then try to use it to retrieve the value, the value won’t be there. (Also, though it’s not in the gist: if you mutate the array back, it will be there again.)

I don’t think I’ve ever used and array as a hash key in C#, but I wanted to give it a try in C#’s beloved Dictionary<TKey, TValue> to see how it worked:

using System;
using System.Collections.Generic;
namespace array_compare
class Program
static void DoesItChange()
Dictionary<int[], string> hash = new Dictionary<int[], string>();
int[] mutableArray = new int[] { 1,2 };
hash[mutableArray] = "foo";
Console.WriteLine(hash[mutableArray]); // prints "foo"
mutableArray[1] = 3;
Console.WriteLine(hash[mutableArray]); // prints "foo"
static void Main(string[] args)

What’s going on is that Ruby, and I believe C#, both use the object reference as the input to their hash function. When they hash a complex object, they’re using a pointer to the object as the input to the hash function. In Ruby, however, arrays are special, and their hash function is calculated based on the value of their contents.

As a sanity check, I ran similar experiments on the whether the arrays are equal, and was relieved to see that this yields the same results as the hash function: In Ruby, arrays with the same contents are equal, whereas in C# arrays that are the same object reference are equal. That’s a relief. Also, this exercise gave me new appreciation for the fact that the C# compiler emits a warning if you define a class that overrides Equals() but not GetHashCode() (or vice versa).

That was my second surprising linguistic difference between Ruby and C#. (The first was that you can’t call class methods from instance methods.) Anyway, I’m still having a blast, and have very high regard for Ruby and C# as languages. I hope knowing this helps you out, or is at least interesting to think about. Also, I don’t really recommend using arrays as hash keys, but I definitely don’t recommend doing it in Ruby.

Till next time, happy learning!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s