Thread overview
Referencing structs in a foreach
Jul 23, 2014
Alf
Jul 23, 2014
Dicebot
Jul 23, 2014
Alf
Jul 23, 2014
Peter Alexander
Jul 23, 2014
Dicebot
July 23, 2014
I am wondering what could be the mistake. When trying to modify the member of a struct from within a foreach, the changes are gone after leaving the loop.

Code:

import std.stdio;

void test()
{
  struct Foo
  {
    int a;
    int b;
  }

  struct Bar
  {
    Foo f1;
    Foo f2;
  }

  Bar bar = {{10, 20}, {10, 20}};

  writefln("Before => f1.a: %d, f1.b: %d - f2.a: %d, f2.b: %d\n", bar.f1.a, bar.f1.b, bar.f2.a, bar.f2.b);

  foreach (ref f; [bar.f1, bar.f2])
  {
    f.a++;
    f.b++;
  }

  writefln("After => f1.a: %d, f1.b: %d - f2.a: %d, f2.b: %d\n", bar.f1.a, bar.f1.b, bar.f2.a, bar.f2.b);
}

void main()
{
  test();
}

Should print:

Before => f1.a: 10, f1.b: 20 - f2.a: 10, f2.b: 20
After => f1.a: 11, f1.b: 21 - f2.a: 11, f2.b: 21

But prints:

Before => f1.a: 10, f1.b: 20 - f2.a: 10, f2.b: 20
After => f1.a: 10, f1.b: 20 - f2.a: 10, f2.b: 20

Thanks,

Alf
July 23, 2014
On Wednesday, 23 July 2014 at 01:31:48 UTC, Alf wrote:
>   foreach (ref f; [bar.f1, bar.f2])

This allocates new array and copies struct values to it (as D structs are value types). I think this should have been a compile-time error btw, it never makes sense to do ref iteration over an array literal for this very reason.

Most likely you want to forced loop unrolling here and thus something like this:

>   import std.typetuple;
>   foreach (ref f; TypeTuple!(bar.f1, bar.f2))

(don't pay attention to the weird "TypeTyple" name, it lies)
July 23, 2014
Also please post any further questions to http://forum.dlang.org/group/digitalmars.D.learn instead
July 23, 2014
Thanks for your reply.
I figured an easier way would be to simply build an array of references:

  foreach (ref f; [&bar.f1, &bar.f2])
  {
    f.a++;
    f.b++;
  }

It seems to work just fine.

Alf

On Wednesday, 23 July 2014 at 01:41:33 UTC, Dicebot wrote:
> On Wednesday, 23 July 2014 at 01:31:48 UTC, Alf wrote:
>>  foreach (ref f; [bar.f1, bar.f2])
>
> This allocates new array and copies struct values to it (as D structs are value types). I think this should have been a compile-time error btw, it never makes sense to do ref iteration over an array literal for this very reason.
>
> Most likely you want to forced loop unrolling here and thus something like this:
>
>>  import std.typetuple;
>>  foreach (ref f; TypeTuple!(bar.f1, bar.f2))
>
> (don't pay attention to the weird "TypeTyple" name, it lies)

July 23, 2014
On Wednesday, 23 July 2014 at 17:23:14 UTC, Alf wrote:
> Thanks for your reply.
> I figured an easier way would be to simply build an array of references:
>
>   foreach (ref f; [&bar.f1, &bar.f2])
>   {
>     f.a++;
>     f.b++;
>   }
>
> It seems to work just fine.

I'd recommend using std.range.only:

foreach (ref f; only(&x, &y)) {
    f.a++;
    f.b++;
}

Using array literals introduces the possibility of an unnecessary heap allocation. Using only will never allocate.