Class: ObjectSpace::WeakKeyMap
Overview
An ObjectSpace::WeakKeyMap is a key-value map that holds weak references to its keys, so they can be garbage collected when there is no more references.
Unlike ObjectSpace::WeakMap:
- 
references to values are strong, so they aren’t garbage collected while they are in the map; 
- 
keys are compared by value (using Object#eql?), not by identity; 
- 
only garbage-collectable objects can be used as keys. map = ObjectSpace::WeakKeyMap.new val = Time.new(2023, 12, 7) key = "name" map[key] = val # Value is fetched by equality: the instance of string "name" is # different here, but it is equal to the key map["name"] #=> 2023-12-07 00:00:00 +0200 val = nil GC.start # There are no more references to `val`, yet the pair isn't # garbage-collected. map["name"] #=> 2023-12-07 00:00:00 +0200 key = nil GC.start # There are no more references to `key`, key and value are # garbage-collected. map["name"] #=> nil
(Note that GC.start is used here only for demonstrational purposes and might not always lead to demonstrated results.)
The collection is especially useful for implementing caches of lightweight value objects, so that only one copy of each value representation would be stored in memory, but the copies that aren’t used would be garbage-collected.
CACHE = ObjectSpace::WeakKeyMap
def make_value(**)
   val = ValueObject.new(**)
   if (existing = @cache.getkey(val))
      # if the object with this value exists, we return it
      existing
   else
      # otherwise, put it in the cache
      @cache[val] = true
      val
   end
end
This will result in make_value returning the same object for same set of attributes always, but the values that aren’t needed anymore wouldn’t be sitting in the cache forever.
Instance Method Summary collapse
- 
  
    
      #[](key)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    Returns the value associated with the given keyif found.
- 
  
    
      #[]=(key)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    Associates the given valuewith the givenkey.
- 
  
    
      #clear  ⇒ self 
    
    
  
  
  
  
  
  
  
  
  
    Removes all map entries; returns self.
- 
  
    
      #delete(key)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    Deletes the entry for the given keyand returns its associated value.
- 
  
    
      #getkey(key)  ⇒ nil 
    
    
  
  
  
  
  
  
  
  
  
    Returns the existing equal key if it exists, otherwise returns nil.
- 
  
    
      #inspect  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    Returns a new String containing informations about the map:. 
- 
  
    
      #key?(key)  ⇒ Boolean 
    
    
  
  
  
  
  
  
  
  
  
    Returns trueifkeyis a key inself, otherwisefalse.
Instance Method Details
#[](key) ⇒ Object
Returns the value associated with the given key if found.
If key is not found, returns nil.
| 816 817 818 819 820 821 | # File 'weakmap.c', line 816
static VALUE
wkmap_aref(VALUE self, VALUE key)
{
    VALUE obj = wkmap_lookup(self, key);
    return !UNDEF_P(obj) ? obj : Qnil;
} | 
#[]=(key) ⇒ Object
Associates the given value with the given key
The reference to key is weak, so when there is no other reference to key it may be garbage collected.
If the given key exists, replaces its value with the given value; the ordering is not affected
| 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 | # File 'weakmap.c', line 855
static VALUE
wkmap_aset(VALUE self, VALUE key, VALUE val)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
    if (!FL_ABLE(key) || SYMBOL_P(key) || RB_BIGNUM_TYPE_P(key) || RB_TYPE_P(key, T_FLOAT)) {
        rb_raise(rb_eArgError, "WeakKeyMap keys must be garbage collectable");
        UNREACHABLE_RETURN(Qnil);
    }
    struct wkmap_aset_args args = {
        .new_key = key,
        .new_val = val,
    };
    st_update(w->table, (st_data_t)&key, wkmap_aset_replace, (st_data_t)&args);
    RB_OBJ_WRITTEN(self, Qundef, key);
    RB_OBJ_WRITTEN(self, Qundef, val);
    return val;
} | 
#clear ⇒ self
Removes all map entries; returns self.
| 995 996 997 998 999 1000 1001 1002 1003 1004 1005 | # File 'weakmap.c', line 995
static VALUE
wkmap_clear(VALUE self)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
    st_foreach(w->table, wkmap_clear_i, (st_data_t)self);
    st_clear(w->table);
    return self;
} | 
#delete(key) ⇒ nil #delete(key) {|key| ... } ⇒ Object
Deletes the entry for the given key and returns its associated value.
If no block is given and key is found, deletes the entry and returns the associated value:
m = ObjectSpace::WeakKeyMap.new
key = "foo" # to hold reference to the key
m[key] = 1
m.delete("foo") # => 1
m["foo"] # => nil
If no block given and key is not found, returns nil.
If a block is given and key is found, ignores the block, deletes the entry, and returns the associated value:
m = ObjectSpace::WeakKeyMap.new
key = "foo" # to hold reference to the key
m[key] = 2
m.delete("foo") { |key| raise 'Will never happen'} # => 2
If a block is given and key is not found, yields the key to the block and returns the block’s return value:
m = ObjectSpace::WeakKeyMap.new
m.delete("nosuch") { |key| "Key #{key} not found" } # => "Key nosuch not found"
| 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 | # File 'weakmap.c', line 908
static VALUE
wkmap_delete(VALUE self, VALUE key)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
    VALUE orig_key = key;
    st_data_t orig_key_data = (st_data_t)&orig_key;
    st_data_t orig_val_data;
    if (st_delete(w->table, &orig_key_data, &orig_val_data)) {
        VALUE orig_val = (VALUE)orig_val_data;
        rb_gc_remove_weak(self, (VALUE *)orig_key_data);
        ruby_sized_xfree((VALUE *)orig_key_data, sizeof(VALUE));
        return orig_val;
    }
    if (rb_block_given_p()) {
        return rb_yield(key);
    }
    else {
        return Qnil;
    }
} | 
#getkey(key) ⇒ nil
Returns the existing equal key if it exists, otherwise returns nil.
This might be useful for implementing caches, so that only one copy of some object would be used everywhere in the program:
value = {amount: 1, currency: 'USD'}
# Now if we put this object in a cache:
cache = ObjectSpace::WeakKeyMap.new
cache[value] = true
# ...we can always extract from there and use the same object:
copy = cache.getkey({amount: 1, currency: 'USD'})
copy.object_id == value.object_id #=> true
| 954 955 956 957 958 959 960 961 962 963 964 | # File 'weakmap.c', line 954
static VALUE
wkmap_getkey(VALUE self, VALUE key)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
    st_data_t orig_key;
    if (!st_get_key(w->table, (st_data_t)&key, &orig_key)) return Qnil;
    return *(VALUE *)orig_key;
} | 
#inspect ⇒ Object
Returns a new String containing informations about the map:
m = ObjectSpace::WeakKeyMap.new
m[key] = value
m.inspect # => "#<ObjectSpace::WeakKeyMap:0x00000001028dcba8 size=1>"
| 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 | # File 'weakmap.c', line 1018
static VALUE
wkmap_inspect(VALUE self)
{
    struct weakkeymap *w;
    TypedData_Get_Struct(self, struct weakkeymap, &weakkeymap_type, w);
    st_index_t n = st_table_size(w->table);
#if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
    const char * format = "#<%"PRIsVALUE":%p size=%lu>";
#else
    const char * format = "#<%"PRIsVALUE":%p size=%llu>";
#endif
    VALUE str = rb_sprintf(format, rb_class_name(CLASS_OF(self)), (void *)self, n);
    return str;
} | 
#key?(key) ⇒ Boolean
Returns true if key is a key in self, otherwise false.
| 972 973 974 975 976 | # File 'weakmap.c', line 972
static VALUE
wkmap_has_key(VALUE self, VALUE key)
{
    return RBOOL(!UNDEF_P(wkmap_lookup(self, key)));
} |