Why is `opequals` for objects not `@safe` by default?

Liam McGillivray yoshi.pit.link.mario at gmail.com
Sun Apr 7 21:05:14 UTC 2024


On Sunday, 7 April 2024 at 09:59:56 UTC, Jonathan M Davis wrote:
> No, we really don't want to force @safe on opEquals, because 
> that means that you can't write one that's @system, meaning 
> that you're potentially forced to use @trusted on code that 
> really shouldn't be treated as @safe.

Well, I already ended up putting `@trusted` in a function I 
didn't want to put it in because `opEquals` was `@system`. Point 
taken though.

> ```
> class C
> {
>     bool opEquals(C rhs) @safe
>     {
>         ...
>     }
>
>     override bool opEquals(Object rhs) @safe
>     {
>         ...
>     }
> }
> ```
> then the first overload will be called when comparing class 
> references of type C - or class references derived from C, 
> whereas if you're comparing class references of type Object (or 
> class references which aren't C or derived from C), then 
> they'll be compared as Object, and the base class opEquals will 
> be called, resulting in your second overload being called 
> thanks to polymorphism, but because the base class version is 
> @system, == will still be treated as @system in that case. E.G.

Oh! Is this how the `opEquals` function used can be made to 
depend on the reference type, rather than the type of the object 
contained in the reference?

>> Anyway, I discovered that `==` can be replaced with `is`, and 
>> it seems to do the same thing while working within a `@safe` 
>> function.
>
> The is operator and == are _not_ the same. When you use is on 
> class references, it's true if and only if the two references 
> point to the same object. It's basically doing a pointer 
> comparison. In contrast, == calls the free function, opEquals, 
> which will call opEquals on the class references to compare 
> them if they're not null and they're not pointing to the same 
> object.

Right. I was just saying that `is` will in most cases give the 
same results as the default `opEquals` function. The existence of 
`is` means I no longer think `opEquals` should be `@safe` by 
default. The only place where they would differ is when the first 
reference is null, right?

Ultimately, not knowing to use `is` is what lead me to making 
this topic.

Actually, I just looked in [the base object 
definition](https://github.com/dlang/dmd/blob/master/druntime/src/object.d), and I found this:
```
     bool opEquals(Object o)
     {
         return this is o;
     }
```
It looks like it really would be the same whenever the first 
reference is a valid object. I figure it would segfault if the 
first reference is null, while `is` would simply return `false`.

> You already need to do an override to get opEquals unless you 
> want it to just compare the references, which is borderline 
> useless. All that opEquals on Object does is compare the 
> addresses of the references with the is operator, and that's 
> almost never what you want when doing an equality check.

Well, it's exactly what I wanted, but I'm new to the language, 
and hadn't yet been taught to use `is` in such cases. For the 
function I was writing, see `Weapon.getOptions` or look for `if 
(user.currentWeapon == this)` in [this 
module](https://github.com/LiamM32/Open_Emblem/blob/master/source/item.d).

Now knowing about the proper uses of `==` and `is`, I think the 
only worthy idea I have for a new addition to the language is a 
new function in `std.algorithm.searching`; a `@safe bool` 
function called `contains`. It would return whether the first 
argument (an array of the second argument's type) contains the 
second argument. It would be equivalent to `canFind` for some 
argument types, but for objects it would do an `is` comparison.

Perhaps I can try to implement this myself. Given my 
inexperience, chances are someone else would make some edits to 
it before getting merged into Phobos.



More information about the Digitalmars-d mailing list