Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Escaping from "inout hell"

Tags:

d

I have some very simple code that refuses to compile:

struct Wrapper(T)
{
    T t;

    bool opEquals(inout(Wrapper) other) inout
    {
        return t == other.t;
    }

    bool opEquals(inout(T) val) inout
    {
        return t == val;
    }
}

struct Test
{
    bool opEquals(Test t)
    {
        return true;
    }
}

void main()
{
    Wrapper!Test a, b;

    assert(a == b);

    //Error: inout method Test.opEquals is not 
    //callable using a mutable object
    assert(a == Test());
}

Now, I know the problem, which is that Test does not defin an inout opEquals. However, defining another mutable version of opEquals in Test does not fix this problem, as the compiler just ignores it and calls the inout version regardless. Is there any way for me to fix this problem without resorting to defining an opEquals overload for mutable, const, and immutable?

like image 815
Meta Avatar asked Nov 28 '25 23:11

Meta


1 Answers

All inout is for is making it so that the constness of the return type can match that of a function argument. If you have

const(Foo) bar(const(Foo) f) {
{
    //...
    return f;
}

and you pass a mutable or immutable object to bar, you end up with a const object returned, whereas if you use inout

inout(Foo) bar(inout(Foo) f) {
{
    //...
    return f;
}

the return type has the same constness as the argument passed to f. Either way, within the function, f is effectively treated as const. You can only call const and inout functions on it. So, making opEquals inout is pointless, because it doesn't return any of its arguments. It's the same as making it const.

Your fundamental problem here is that you're trying to call a mutable function on a const object. And that's not legal, because it violates const. You have one of two options:

  1. Make Wrapper's opEquals mutable. Then it can call opEquals when T's opEquals is mutable.

  2. Use static if to define opEquals differently depending on how T defined opEquals.

There is no way to forward the constness of opEquals from T to Wrapper without doing it explicitly with static if. e.g.

struct Wrapper(T)
{
    T t;

    static if(is(typeof({const T t; t == t;})))
    {
        bool opEquals(const Wrapper other) const
        {
            return t == other.t;
        }

        bool opEquals(const T val) const
        {
            return t == val;
        }
    }
    else
    {
        bool opEquals(Wrapper other)
        {
            return t == other.t;
        }

        bool opEquals(T val)
        {
            return t == val;
        }
    }
}

Because Wrapper is a template, pure, nothrow, and @safe will be inferred on its functions, but there is no attribute inference for const, inout, or immutable.

like image 125
Jonathan M Davis Avatar answered Dec 02 '25 03:12

Jonathan M Davis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!