Why D const is annoying

Steven Schveighoffer schveiguy at yahoo.com
Mon Dec 12 08:46:10 PST 2011


On Mon, 12 Dec 2011 10:21:35 -0500, Timon Gehr <timon.gehr at gmx.ch> wrote:

> On 12/12/2011 04:08 PM, Timon Gehr wrote:
>> On 12/12/2011 03:46 PM, Timon Gehr wrote:
>>> On 12/12/2011 01:50 PM, Steven Schveighoffer wrote:
>>>> On Sun, 11 Dec 2011 12:07:37 -0500, Mafi <mafi at example.org> wrote:
>>>>
>>>>> Am 10.12.2011 21:25, schrieb Walter Bright:
>>>>>> On 12/10/2011 11:03 AM, Mehrdad wrote:
>>>>>>> So how are you supposed to implement opApply on a container (or  
>>>>>>> e.g.
>>>>>>> here, a
>>>>>>> matrix)? Copy/paste the code for const- and non-const versions?
>>>>>>
>>>>>> Internal to a function, inout behaves like 'const'. You won't be
>>>>>> able to
>>>>>> modify the data. Therefore, if there is no inout in the return type,
>>>>>> use
>>>>>> 'const' in the parameter list instead.
>>>>>>
>>>>>> The purpose of inout is to transmit the 'constness' of the function
>>>>>> argument type to the return type, using only one implementation of
>>>>>> that
>>>>>> function. That requires the function to internally regard inout as
>>>>>> const.
>>>>>
>>>>> But what about:
>>>>> void f(ref inout(int)* a, inout(int)* b) { a = b; }
>>>>> This cant work with const because that would violate the const  
>>>>> system.
>>>>> I think the rule should be that either the return type must be inout
>>>>> or at least one ref/out parameter.
>>>>> Am I overlooking something?
>>>>
>>>> That was brought up during discussion on adding the feature. One of  
>>>> the
>>>> reasons inout is viable is because a) the source and result of where  
>>>> the
>>>> constancy flows is well defined and b) the exit point is an rvalue
>>>>
>>>> Allowing ref parameters fails both those rules.
>>>>
>>>> Consider this:
>>>>
>>>> void bad(ref inout(int)* a, ref inout(int)* b);
>>>>
>>>> which is the entry and which is the exit? Is a set to b, or b set to  
>>>> a?
>>>>
>>>> Now, also consider that you can't affect the constancy of the result,
>>>> because the type of the parameter is already defined. e.g.:
>>>>
>>>> // note that your example shouldn't even be valid, because you can't
>>>> implicitly cast through two mutable references
>>>> int a;
>>>> auto pa = &a;
>>>> immutable int b;
>>>> auto pb = &b;
>>>>
>>>> f(a, b);
>>>>
>>>> How can this affect a? its type is already decided. Compare that to:
>>>>
>>>> inout(int)* g(inout(int)* b) { return b;}
>>>>
>>>> auto pa = g(pb);
>>>>
>>>> clean and simple.
>>>>
>>>> -Steve
>>>
>>> This currently compiles:
>>>
>>> inout(void) very_bad(ref inout(int)* a, ref inout(int)* b){a = b;}
>>>
>>> void main(){
>>> immutable int a=2;
>>> int *x;
>>> immutable(int)* y=&a;
>>> very_bad(x,y);
>>> *x=1;
>>> assert(*y==a); // fail
>>> }
>>>
>>> How does the design catch this error? Will inout** = inout**  
>>> assignments
>>> be disallowed?
>>
>> Probably it is better to catch it at the call site. But if that is
>> implemented then the requirement to have inout on the return value gets
>> nonsensical.
>
> OK, got it.
>
> What you call 'bad' should compile:
>
> void bad(ref inout(int)* a, ref inout(int)* b);
>
> int* x;
> immutable(int)* y;
> const(int)* z;
>
> bad(x,x); // fine
> bad(y,y); // fine
> bad(z,z); // fine
>
> bad(x,y); // inout is deduced to const, ergo neither x nor y convert to  
> the parameter type -> compile error
> bad(x,z); // now only error for x
> ...
>
>
> The requirement of having inout on the return type should be removed for  
> more expressiveness.

I've thought about this for a few minutes, and I can't find a flaw in it.   
But I'm not seeing a huge benefit to it either.  Why is it advantageous to  
use a ref parameter for something that should be a return?  Can you show a  
good use case for this?  I still am uneasy with allowing applying a  
wildcard to a reference underneath to mutable references.  I know it  
doesn't work for const.

-Steve


More information about the Digitalmars-d mailing list