Search code examples
constantsdtype-safety

How can I return imutable reference types without break constness?


I have the following code in D:

import std.stdio;
import std.container.array;

class RefType { }

class MyContainer {
    private Array!RefType test;
    RefType result() const {  // I want const on this, not on return type
        return test[0];       // use opIndex() from Array(T)
        // Error: cannot implicitly convert expression (this.test.opIndex(0u)) 
        // of type const(RefType) to main.RefType
    }
}

int main(string[] argv) {
    auto c = new MyContainer; auto r = c.result(); return 0;
}

As you can see I want to return a reference type from a custom container class. But the opIndex() of Array is not giving the permission to do that, why?

I think that opIndex() should return a RefType instead of a const(RefType) value because the Array is Array!RefType.

Is this a bug? or is the design intention? If this is the intented design, how can I get that I want?


Solution

  • I think that opIndex() should return a RefType instead of a const(RefType) value because the Array is Array!RefType.

    No, this is wrong. When you define your memthod to be const, you have also specified that all members of the class are also const. This means that, no matter what type the member has in the definition, inside your result method they are (at least) const. As such, the compiler is completely justified in returning the error it is returning.

    The usual (e.g. C++) way to resolve this is to define two (or, in D, three) overloads:

    RefType result() {
        return test[0];
    }
    RefType result() const {
        return test[0];
    }
    RefType result() immutable {
        return test[0];
    }
    

    So, one for the case the class is mutable, which returns a mutable reference. One for the case the class is const, which returns a const reference, and one for the case the class is immutable, which returns an immutable reference.

    You will notice, however, that aside from the method decoration, all three implementations are precisely the same. To prevent you from defining three identical implementations, D has the inout modifier:

    RefType result() inout {
        return test[0];
    }
    

    This single definition replaces all three of the definitions above. As a rule of thumb, just write your definition as usual, but place inout any place you'd otherwise place const.