Thread overview
Capturing by reference with "visit" from std.variant
Feb 20, 2018
Smaehtin
Feb 20, 2018
Radu
Feb 20, 2018
Smaehtin
Feb 20, 2018
Smaehtin
February 20, 2018
I'm trying to understand why the following doesn't work:

import std.stdio;
import std.variant;

void main()
{
    Algebraic!(string, int) test = "Test";

    test.tryVisit!(
        (ref string s) { s = "Why does this not work?"; }
    );

    writeln(test);
}

But this works fine:
*test.peek!string = "Works fine";

As far as I can tell, the "visit" template expands to something that ends up calling my handler like this:
if (auto ptr = variant.peek!T)
{
    handler(*ptr);
}

But seeing as the handler in my case takes a reference, shouldn't that work just fine? What am I missing?
February 20, 2018
On Tuesday, 20 February 2018 at 16:01:11 UTC, Smaehtin wrote:
> I'm trying to understand why the following doesn't work:
>
> import std.stdio;
> import std.variant;
>
> void main()
> {
>     Algebraic!(string, int) test = "Test";
>
>     test.tryVisit!(
>         (ref string s) { s = "Why does this not work?"; }
>     );
>
>     writeln(test);
> }
>
> But this works fine:
> *test.peek!string = "Works fine";
>
> As far as I can tell, the "visit" template expands to something that ends up calling my handler like this:
> if (auto ptr = variant.peek!T)
> {
>     handler(*ptr);
> }
>
> But seeing as the handler in my case takes a reference, shouldn't that work just fine? What am I missing?

Your lambda is called, but you can't change the variant value trough that reference.

Test with:

import std.stdio;
import std.variant;

void main()
{
    Algebraic!(string, int) test = "Test";

    test.tryVisit!(
        (ref string s)
        {
            writeln("Why does this work? ", s);
        }
    );

    writeln(test);
}


You should get:

Why does this work? Test
Test

February 20, 2018
On Tuesday, 20 February 2018 at 16:15:56 UTC, Radu wrote:
> On Tuesday, 20 February 2018 at 16:01:11 UTC, Smaehtin wrote:
>> I'm trying to understand why the following doesn't work:
>>
>> import std.stdio;
>> import std.variant;
>>
>> void main()
>> {
>>     Algebraic!(string, int) test = "Test";
>>
>>     test.tryVisit!(
>>         (ref string s) { s = "Why does this not work?"; }
>>     );
>>
>>     writeln(test);
>> }
>>
>> But this works fine:
>> *test.peek!string = "Works fine";
>>
>> As far as I can tell, the "visit" template expands to something that ends up calling my handler like this:
>> if (auto ptr = variant.peek!T)
>> {
>>     handler(*ptr);
>> }
>>
>> But seeing as the handler in my case takes a reference, shouldn't that work just fine? What am I missing?
>
> Your lambda is called, but you can't change the variant value trough that reference.
>
> Test with:
>
> import std.stdio;
> import std.variant;
>
> void main()
> {
>     Algebraic!(string, int) test = "Test";
>
>     test.tryVisit!(
>         (ref string s)
>         {
>             writeln("Why does this work? ", s);
>         }
>     );
>
>     writeln(test);
> }
>
>
> You should get:
>
> Why does this work? Test
> Test

Then my question is: Why can't I change the variant through the reference in the lambda?
Seeing as this works just fine:

void main()
{
    Algebraic!(string, int) test = "Test";
    changeIt(*test.peek!string);

    writeln(test);
}

void changeIt(ref string s)
{
    s = "Changed";
}

Prints: Changed
February 20, 2018
On Tuesday, 20 February 2018 at 16:20:45 UTC, Smaehtin wrote:
> On Tuesday, 20 February 2018 at 16:15:56 UTC, Radu wrote:
>> On Tuesday, 20 February 2018 at 16:01:11 UTC, Smaehtin wrote:
>>> I'm trying to understand why the following doesn't work:
>>>
>>> import std.stdio;
>>> import std.variant;
>>>
>>> void main()
>>> {
>>>     Algebraic!(string, int) test = "Test";
>>>
>>>     test.tryVisit!(
>>>         (ref string s) { s = "Why does this not work?"; }
>>>     );
>>>
>>>     writeln(test);
>>> }
>>>
>>> But this works fine:
>>> *test.peek!string = "Works fine";
>>>
>>> As far as I can tell, the "visit" template expands to something that ends up calling my handler like this:
>>> if (auto ptr = variant.peek!T)
>>> {
>>>     handler(*ptr);
>>> }
>>>
>>> But seeing as the handler in my case takes a reference, shouldn't that work just fine? What am I missing?
>>
>> Your lambda is called, but you can't change the variant value trough that reference.
>>
>> Test with:
>>
>> import std.stdio;
>> import std.variant;
>>
>> void main()
>> {
>>     Algebraic!(string, int) test = "Test";
>>
>>     test.tryVisit!(
>>         (ref string s)
>>         {
>>             writeln("Why does this work? ", s);
>>         }
>>     );
>>
>>     writeln(test);
>> }
>>
>>
>> You should get:
>>
>> Why does this work? Test
>> Test
>
> Then my question is: Why can't I change the variant through the reference in the lambda?
> Seeing as this works just fine:
>
> void main()
> {
>     Algebraic!(string, int) test = "Test";
>     changeIt(*test.peek!string);
>
>     writeln(test);
> }
>
> void changeIt(ref string s)
> {
>     s = "Changed";
> }
>
> Prints: Changed

Okay, so I realized this happens because the variant parameter is getting passed down to the "visitImpl" function by value.

Wouldn't it make sense to pass the arguments using "auto ref" in this case?
This is the code I'm talking about: https://github.com/dlang/phobos/blob/9021bd36b97d247cba1def9ce12eca64efee61a5/std/variant.d#L2170

Changing it to "auto ref" would allow capturing by reference in the handlers, but I'm not really sure if it would have any negative side-effects.