Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Immutable keys make sense in general because their hash codes will be stable.</p> <p>This is why strings are specially-converted, in this part of MRI code:</p> <pre><code>if (RHASH(hash)-&gt;ntbl-&gt;type == &amp;identhash || rb_obj_class(key) != rb_cString) { st_insert(RHASH(hash)-&gt;ntbl, key, val); } else { st_insert2(RHASH(hash)-&gt;ntbl, key, val, copy_str_key); } </code></pre> <p>In a nutshell, in the string-key case, <code>st_insert2</code> is passed a pointer to a function that will trigger the dup and freeze.</p> <p>So if we theoretically wanted to support immutable lists and immutable hashes as hash keys, then we could modify that code to something like this:</p> <pre><code>VALUE key_klass; key_klass = rb_obj_class(key); if (key_klass == rb_cArray || key_klass == rb_cHash) { st_insert2(RHASH(hash)-&gt;ntbl, key, val, freeze_obj); } else if (key_klass == rb_cString) { st_insert2(RHASH(hash)-&gt;ntbl, key, val, copy_str_key); } else { st_insert(RHASH(hash)-&gt;ntbl, key, val); } </code></pre> <p>Where <code>freeze_obj</code> would be defined as:</p> <pre><code>static st_data_t freeze_obj(st_data_t obj) { return (st_data_t)rb_obj_freeze((VALUE) obj); } </code></pre> <p>So that would solve the specific inconsistency that you observed, where the array-key was mutable. However to be really consistent, more types of objects would need to be made immutable as well.</p> <p>Not <em>all</em> types, however. For example, there'd be no point to freezing immediate objects like Fixnum because there is effectively only one instance of Fixnum corresponding to each integer value. This is why only <code>String</code> needs to be special-cased this way, not <code>Fixnum</code> and <code>Symbol</code>.</p> <p>Strings are a special exception simply as a matter of convenience for Ruby programmers, because strings are very often used as hash keys.</p> <p>Conversely, the reason that other object types are <em>not</em> frozen like this, which admittedly leads to inconsistent behavior, is mostly a matter of convenience for Matz &amp; Company to not support edge cases. In practice, comparatively few people will use a container object like an array or a hash as a hash key. So if you do so, it's up to you to freeze before insertion.</p> <p>Note that this is not strictly about performance, because the act of freezing a non-immediate object simply involves flipping the <code>FL_FREEZE</code> bit on the <code>basic.flags</code> bitfield that's present on every object. That's of course a cheap operation.</p> <p>Also speaking of performance, note that if you are going to use string keys, and you are in a performance-critical section of code, you might want to freeze your strings before doing the insertion. If you don't, then a dup is triggered, which is a more-expensive operation.</p> <p><strong>Update</strong> @sawa pointed out that leaving your array-key simply frozen means the original array might be unexpectedly immutable outside of the key-use context, which could also be an unpleasant surprise (although otoh it would serve you right for using an array as a hash-key, really). If you therefore surmise that dup + freeze is the way out of that, then you would in fact incur possible noticeable performance cost. On the third hand, leave it unfrozen altogether, and you get the OP's original weirdness. Weirdness all around. Another reason for Matz et al to defer these edge cases to the programmer.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload