---
title: "Diggin’ and Fetchin’ with TruffleRuby"
description: "The investigation starts with production Ruby code that chains Hash#fetch calls and uses an empty-hash default to return an IdentityObject for missing nested data. It compares that workaround with Hash#dig, which is cleaner but does not provide fetch's same control over missing keys, defaults, and explicit nil values. To prototype a combined Hash#dig_fetch method, the author modifies TruffleRuby, using Primitive's hash_get_or_undefined and undefined? to distinguish absent keys from keys containing nil, then replaces recursive traversal with an iterative implementation in shared Diggable logic. For a hash with nine nested keys, iterative changes lift dig from about 2.5M to 16M iterations per second and lift dig_fetch from about 2.5M to 15.5M. The dig_fetch prototype enables the original refactor, but it is not ready for general Ruby adoption because Array and Struct interoperability and corresponding MRI work remain."
---

# Diggin’ and Fetchin’ with TruffleRuby

[Shopify](https://yomu.fyi/company/shopify) · 2023-10-18 · Aug 20, 2021

**Type:** Explainer

## Summary

The investigation starts with production Ruby code that chains Hash#fetch calls and uses an empty-hash default to return an IdentityObject for missing nested data. It compares that workaround with Hash#dig, which is cleaner but does not provide fetch's same control over missing keys, defaults, and explicit nil values. To prototype a combined Hash#dig\_fetch method, the author modifies TruffleRuby, using Primitive's hash\_get\_or\_undefined and undefined? to distinguish absent keys from keys containing nil, then replaces recursive traversal with an iterative implementation in shared Diggable logic. For a hash with nine nested keys, iterative changes lift dig from about 2.5M to 16M iterations per second and lift dig\_fetch from about 2.5M to 15.5M. The dig\_fetch prototype enables the original refactor, but it is not ready for general Ruby adoption because Array and Struct interoperability and corresponding MRI work remain.

## Context

Production Ruby code used chained Hash#fetch calls with an empty-hash default to retrieve nested data, but the pattern was difficult to read. Hash#dig offered a cleaner traversal, yet it lacked fetch's flexibility for raising errors, returning defaults, and distinguishing missing keys from keys explicitly set to nil. The investigation also uncovered a performance issue in TruffleRuby's recursive dig implementation.

## Approach / What changed

The author prototyped Hash#dig\_fetch in TruffleRuby, using Primitive's hash\_get\_or\_undefined and undefined? methods to distinguish missing keys from explicit nil values. The implementation was then refactored from recursive traversal to iterative logic in a shared Diggable package, improving both dig and dig\_fetch performance. The resulting changes were shipped to TruffleRuby for dig, while dig\_fetch remained a prototype.

## Takeaways

- Hash#fetch can return a supplied default, including an empty hash, while Hash#dig returns nil for missing keys and does not provide the same built-in control over errors, defaults, and explicit nil values.
- TruffleRuby's Primitive methods hash\_get\_or\_undefined and undefined? distinguish missing hash keys from keys whose values are explicitly nil.
- For a hash with nine nested keys, iterative traversal improved dig from about 2.5M to 16M iterations per second and dig\_fetch from about 2.5M to 15.5M.

**Tags:** [Performance](https://yomu.fyi/topic/performance), [Refactoring](https://yomu.fyi/topic/refactoring)

- Source: [Shopify](https://shopify.engineering/dig-fetch-truffleruby)
- Source URL: https://shopify.engineering/dig-fetch-truffleruby
- Ingested by Yomu: 2026-08-30T15:29:26.784Z

[Read original post](https://shopify.engineering/dig-fetch-truffleruby)
