Here is the proposal for Ruby 4.

Hash#[] and Array#[] have to be renamed to something that is less convenient to use. For example, to Hash#get and Array#get. These methods should either accept 2 parameters (a key/index and a default value), or one parameter (key/index) and a block which evaluates the default value.

Hash#fetch and Array#fetch have to be renamed to Hash#[] and Array#[].

In other words, {Hash,Array}#[] should fail when an element is missing.

Returning nil by default is an anti-pattern and should be avoided.

But until Ruby 4 is there, we have to be deal with what we have. The simplicity of calling #[] leads to code that is harder to debug in case of errors.

Consider this.

@table[f][i] = i

What if, due to some bug, @table[f] returns nil because of a missing key? Perhaps, f got some unexpected value or something like this? What we will get with this code is NoMethodError.

@table = {a: [1]}

@table['a'][0] = 2
# => NoMethodError: undefined method `[]=' for nil:NilClass

Now, what if we use fetch instead of the first []? It reveals our intent more clearly, and if something goes wrong fails with a better error:

@table = {a: [1]}

@table.fetch('a')[0] = 2
# => KeyError: key not found: "a"

This is for sure a much more descriptive error than the previous one.

I know the code looks ugly. That’s why I proposed changes to the semantics of [] in the beginning of this post.