www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Capturing by reference with "visit" from std.variant

reply Smaehtin <smaehtin invalid.com> writes:
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?
Feb 20 2018
parent reply Radu <void null.pt> writes:
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
Feb 20 2018
parent reply Smaehtin <smaehtin invalid.com> writes:
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
Feb 20 2018
parent Smaehtin <smaehtin invalid.com> writes:
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.
Feb 20 2018