D is about to take a wrong turn with interface identities

Quirin Schroll qs.il.paperinik at gmail.com
Mon Nov 20 16:41:46 UTC 2023


The [spec on identity 
expressions](https://dlang.org/spec/expression.html#identity_expressions) was recently [changed](https://github.com/dlang/dlang.org/pull/3726) to:
> For class / interface objects, identity is defined as the 
> object references being identical. Null class objects can be 
> compared with `is`. Note that inferface objects need not have 
> the same reference of the class they were cast from. To test 
> whether an `interface` shares a class instance with another 
> `interface` / `class` value, cast both operands to `Object` 
> before comparing with `is`.

My sense is that this behavior is surprising and therefore 
unacceptable. Even the route of simply casting to `void*` is 
unacceptable as it will surprise C++ users who know that 
`dynamic_cast<void*>`, given a pointer to a base-type subobject, 
returns a `void*` to the actual (most-derived) object; that way, 
two seemingly unrelated class-type objects can be compared for 
identity without knowing their most-derived type. D doesn’t need 
that, given that `Object` is a known base class for all classes, 
a cast to `Object` achieves the same. If anything, D’s 
`cast(void*)` should do the same as it does in C++, or be 
invalid. As was [pointed out to 
me](https://forum.dlang.org/post/gitvtdwiygpuvwduomfc@forum.dlang.org), a type paint should be done as per `*cast(void**)(&obj)`. DRuntime or Phobos could provide this via a convenience function `reinterpretAsVoidPointer`.

If interface identity cannot be done fast enough and correctly, 
and the language designers don’t want to provide a slow and 
correct solution as an operator, whatever their rationale may be, 
they should not provide an incorrect solution, but rather no 
solution at all, that is, make using the identity operator with 
one or two interface type values an error. The error message 
should tell people to use a `cast(Object)`; for template code, if 
a value is a class or interface, note that `cast(Object)` is a 
no-op on class values.

None of this addresses the problem that the `cast(Object)` cannot 
work for `extern(C++)` classes, even if C++-RTTI was available, 
which it hopefully will in the future. A solution would be to do 
what C++ does: Make `cast(void*)` bypass a potential `opCast` 
(akin to how class-type assignment bypasses `opAssign`) to return 
a pointer to the most-derived object the reference refers to. 
Alternatively, a DRuntime function `dynamicCastVoidPointer` could 
provide this.

Make interfaces easy to use correctly and hard to use 
incorrectly. – Scott Mayers


More information about the Digitalmars-d mailing list