Are nodes variants? Are they hashable?

Godot Version

v4.2.2.stable.mono.official [15073afe3]

Question

I’m trying to understand some apparent contradictions in the documentation. (Likely I am just misunderstanding something)

  1. Dictionary keys are of type Variant
    Dictionary — Godot Engine (stable) documentation in English

  2. Variants are hashable
    Variant — Godot Engine (stable) documentation in English

  3. Nodes can be used as the keys of dictionaries
    my_dict[my_node] = true # works fine

  4. Nodes do not provide a .hash() function

What am I missing?
How are nodes even able to be used as keys if they aren’t hashable?
If they are hashable, how do I get my hands on their hash?

I can’t really tell if Variant is just what GDscript calls a variable, or if they’re some kind of object that other objects are “boxed up” in.

Dictionary keys are not of the type variant.
Try this code experiment:

	var expr:Dictionary = {1:12, "2":13}
	var a:Array = expr.keys()
	printt(type_string(typeof(a[0])),type_string(typeof(a[1])))

and see the output
int String

Variants Tagged union - Wikipedia are data that boxes in other data. Very important concept in lower-level languages. They contain the data and information about the type of the data. E.g. you could check if a Variant is a float or int and react accordingly. In GDScript if you do not assign a type to a variable it is Variant by default which is a tagged union/variant/sum type of all the basic types GDscript supports. If you have a look at variant.h this should be a list of the types it can box in:

class Variant {
public:
   // If this changes the table in variant_op must be updated
   enum Type {
   	NIL,

   	// atomic types
   	BOOL,
   	INT,
   	FLOAT,
   	STRING,

   	// math types
   	VECTOR2,
   	VECTOR2I,
   	RECT2,
   	RECT2I,
   	VECTOR3,
   	VECTOR3I,
   	TRANSFORM2D,
   	VECTOR4,
   	VECTOR4I,
   	PLANE,
   	QUATERNION,
   	AABB,
   	BASIS,
   	TRANSFORM3D,
   	PROJECTION,

   	// misc types
   	COLOR,
   	STRING_NAME,
   	NODE_PATH,
   	RID,
   	OBJECT,
   	CALLABLE,
   	SIGNAL,
   	DICTIONARY,
   	ARRAY,

   	// typed arrays
   	PACKED_BYTE_ARRAY,
   	PACKED_INT32_ARRAY,
   	PACKED_INT64_ARRAY,
   	PACKED_FLOAT32_ARRAY,
   	PACKED_FLOAT64_ARRAY,
   	PACKED_STRING_ARRAY,
   	PACKED_VECTOR2_ARRAY,
   	PACKED_VECTOR3_ARRAY,
   	PACKED_COLOR_ARRAY,
   	PACKED_VECTOR4_ARRAY,

   	VARIANT_MAX
   };

Dictionary keys need to be of type Variant to support different types of keys (floats, strings and so on). In lower-level languages you can either have your dictionary just accept one kind of key/value or use a Variant type.

There is a global hash function in GDscript. print(hash(get_node("AnimatedSprite2D")))
Nodes should be hashable as far as I can tell. There is also a hash()-function in variant.cpp that handles various cases, Node inherits from Object so I guess the gdscript function ends up at:

case OBJECT: {
			return hash_one_uint64(hash_make_uint64_t(_get_obj().obj));