Thread overview
`this` template params for struct not expressing constness.
Jun 08, 2020
adnan338
Jun 08, 2020
evilrat
Jun 08, 2020
Simen Kjærås
Jun 08, 2020
adnan338
Jun 08, 2020
Simen Kjærås
June 08, 2020
Hi, as far as I understand, the `this` template parameter includes constness qualifiers as seen in https://ddili.org/ders/d.en/templates_more.html

To apply this I have this following struct:

module bst;

struct Tree(T) {
    T item;
    Tree!T* parent, left, right;

    this(T item) {
        this.item = item;
    }

    Self* searchTree(this Self)(auto in ref T item) const {
        if (&this is null)
            return null;
        if (this.item == item)
            return &this;
        return (this.item < item) ? this.right.searchTree(item) : this.right.searchTree(item);
    }
}

unittest {
    auto text1 = "Hello", text2 = "World";

    auto tree2 = Tree!string(text1);
    assert(tree2.searchTree(text2) is null);
    assert(tree2.searchTree(text1) !is null);

    auto tree1 = Tree!int(4);
    assert(tree1.searchTree(5) is null);
    assert(tree1.searchTree(4) !is null);

}


When I run the unittest the compiler complains:

cannot implicitly convert expression &this of type const(Tree!string)* to Tree!string*

Run link: https://run.dlang.io/is/b76DND

Why does this happen?
June 08, 2020
On Monday, 8 June 2020 at 07:35:12 UTC, adnan338 wrote:
> Hi, as far as I understand, the `this` template parameter includes constness qualifiers as seen in https://ddili.org/ders/d.en/templates_more.html
>
> To apply this I have this following struct:
>
> module bst;
>
> struct Tree(T) {
>     T item;
>     Tree!T* parent, left, right;
>
>     this(T item) {
>         this.item = item;
>     }
>
>     Self* searchTree(this Self)(auto in ref T item) const {
>         if (&this is null)
>             return null;
>         if (this.item == item)
>             return &this;
>         return (this.item < item) ? this.right.searchTree(item) : this.right.searchTree(item);
>     }
> }
>
> unittest {
>     auto text1 = "Hello", text2 = "World";
>
>     auto tree2 = Tree!string(text1);
>     assert(tree2.searchTree(text2) is null);
>     assert(tree2.searchTree(text1) !is null);
>
>     auto tree1 = Tree!int(4);
>     assert(tree1.searchTree(5) is null);
>     assert(tree1.searchTree(4) !is null);
>
> }
>
>
> When I run the unittest the compiler complains:
>
> cannot implicitly convert expression &this of type const(Tree!string)* to Tree!string*
>
> Run link: https://run.dlang.io/is/b76DND
>
> Why does this happen?

If I correctly understand what you are trying to do the answer is - in D const is transitive (unlike the C++ where it isn't).

And in your searchTree() method you are basically trying to escape that constness. You can change the signature to return const(Self)*, or maybe add non-const overload depending on your needs.

Don't even think about casting away const with `return cast(Self*) &this;` as this UB in D, and it will bite you somewhere later because compiler might rely on const for optimization.

June 08, 2020
On Monday, 8 June 2020 at 07:35:12 UTC, adnan338 wrote:
>     Self* searchTree(this Self)(auto in ref T item) const {
>         if (&this is null)
>             return null;
>         if (this.item == item)
>             return &this;
>         return (this.item < item) ?
>             this.right.searchTree(item) :
>             this.right.searchTree(item);
>     }

This method is const, which means 'this' is const, while Self is not. What you're looking for here is inout (https://dlang.org/spec/function.html#inout-functions):

    auto searchTree()(auto in ref T item) inout {
        if (&this is null)
            return null;
        if (this.item == item)
            return &this;
        return (this.item < item) ?
            this.right.searchTree(item) :
            this.right.searchTree(item);
    }

--
  Simen
June 08, 2020
On Monday, 8 June 2020 at 08:10:19 UTC, Simen Kjærås wrote:
> On Monday, 8 June 2020 at 07:35:12 UTC, adnan338 wrote:
>>     Self* searchTree(this Self)(auto in ref T item) const {
>>         if (&this is null)
>>             return null;
>>         if (this.item == item)
>>             return &this;
>>         return (this.item < item) ?
>>             this.right.searchTree(item) :
>>             this.right.searchTree(item);
>>     }
>
> This method is const, which means 'this' is const, while Self is not. What you're looking for here is inout (https://dlang.org/spec/function.html#inout-functions):
>
>     auto searchTree()(auto in ref T item) inout {
>         if (&this is null)
>             return null;
>         if (this.item == item)
>             return &this;
>         return (this.item < item) ?
>             this.right.searchTree(item) :
>             this.right.searchTree(item);
>     }
>
> --
>   Simen

Thank you. Few followup questions, if you don't mind.

1. What does that blank template parameter mean?
2. Since `inout` acts as a wildcard for immutable/const/non-const qualifiers, what should I do to have the compiler ensure that my method does not mutate a non-const tree inside the body?

June 08, 2020
On Monday, 8 June 2020 at 09:08:40 UTC, adnan338 wrote:
> On Monday, 8 June 2020 at 08:10:19 UTC, Simen Kjærås wrote:
>> On Monday, 8 June 2020 at 07:35:12 UTC, adnan338 wrote:
>>>     Self* searchTree(this Self)(auto in ref T item) const {
>>>         if (&this is null)
>>>             return null;
>>>         if (this.item == item)
>>>             return &this;
>>>         return (this.item < item) ?
>>>             this.right.searchTree(item) :
>>>             this.right.searchTree(item);
>>>     }
>>
>> This method is const, which means 'this' is const, while Self is not. What you're looking for here is inout (https://dlang.org/spec/function.html#inout-functions):
>>
>>     auto searchTree()(auto in ref T item) inout {
>>         if (&this is null)
>>             return null;
>>         if (this.item == item)
>>             return &this;
>>         return (this.item < item) ?
>>             this.right.searchTree(item) :
>>             this.right.searchTree(item);
>>     }
>>
>> --
>>   Simen
>
> Thank you. Few followup questions, if you don't mind.
>
> 1. What does that blank template parameter mean?

Just forces the function to be a template. The only reason for this is it's required for auto ref to work, which you may or may not need on that function.


> 2. Since `inout` acts as a wildcard for immutable/const/non-const qualifiers, what should I do to have the compiler ensure that my method does not mutate a non-const tree inside the body?

Inside inout functions, `this` is treated as const - any attempt to modify it should give a compile error. Since D const is transitive, anything reachable from `this` is also treated as const.  If you're able to mutate a non-const tree inside the body, there's a bug in the compiler.


--
  Simen