1

In the method reference documentation, there is some example code, copied below:

char8 GetNext()
{
    if (i >= str.Length)
        return 0;
    return str[i++];
}

/* Allocates a delegate bound to GetNext() */
delegate char8() strDlg = scope => GetNext;

strDlg = scope () =>
{
    return 'A';
};

strDlg = scope [&] () =>
{
    return GetNext();
};

/* This delegate owns a string */
String tempStr = new String(str);
tempStr.EnsureNullTerminated();
strDlg = scope [&] () =>
{
    return str[i++];
}
~
{
    delete tempStr;
};

In two of the three examples of lambda definitions, there is a [&] between the allocation expression (scope) and the parameters of the lambda. What is that [&]?

For additional examples, here are some excerpts from tests included in the IDE source:

struct Splattable
{
    public int32 mA = 10;
    public int16 mB = 200;

    public void TestLambda() mut
    {
        delegate int(ref int a, ref int b) dlg = scope [&] (a, b) => 
        {
            a += 20;
            b += 30;
            mA++;
            return mA + a + b;
        };

        mA = 100;
        int testA = 8;
        int testB = 9;
        Test.Assert(dlg(ref testA, ref testB) == 100+1 + 20+8 + 30+9);
        Test.Assert(testA == 28);
        Test.Assert(testB == 39);
        Test.Assert(mA == 101);
    }
}

class ClassA
{
    public int mA;

    public void TestLambda()
    {
        delegate int(ref int a, ref int b) dlg = scope (a, b) => 
        {
            a += 20;
            b += 30;
            mA++;
            return mA + a + b;
        };

        mA = 100;
        int testA = 8;
        int testB = 9;
        Test.Assert(dlg(ref testA, ref testB) == 100+1 + 20+8 + 30+9);
        Test.Assert(testA == 28);
        Test.Assert(testB == 39);
        Test.Assert(mA == 101);
    }
}

Here's another set of example/tests from the IDEHelper part of the source code where every lambda definition has [&]:

using System;

namespace Tests
{
    class Lambdas
    {
        [Test]
        static void TestBasics()
        {
            int a = 1;

            Action act = scope [&] () =>
            {
                Action act2 = scope [&] () =>
                {
                    a += 100;
                };
                act2();
            };
            act();

            Test.Assert(a == 101);
        }

        static int Add3<T>(T func) where T : delegate int()
        {
            return func() + func() + func();
        }

        [Test]
        static void TestValueless()
        {
            Test.Assert(Add3(() => 100) == 300);

            int a = 20;
            int result = Add3(() => a++);
            Test.Assert(result == 63);
        }

        [Test]
        static void LambdaWithDtor()
        {
            int a = 10;
            int b = 20;

            //
            {
                delegate void() dlg = scope [&] () =>
                {
                    a++;
                }
                ~
                {
                    b++;
                };
                dlg();
            }

            Test.Assert(a == 11);
            Test.Assert(b == 21);

            delegate void() dlg = new [&] () =>
            {
                a += 100;
            }
            ~
            {
                b += 200;
            };
            dlg();
            Test.Assert(a == 111);
            Test.Assert(b == 21);
            delete dlg;
            Test.Assert(b == 221);
        }
    }
}

Finally, [&] may be not strictly a lambda thing. I can put it in other definitions like let s = new [&] String(); and it compiles with no issue and as far as I can tell runs with the same outcome as without it. But, I haven't yet seen it occur anywhere else in example/test code.

Ruzihm
  • 19,749
  • 5
  • 36
  • 48
  • I don't know for sure, but I think it's like PHP's `use()` expression that allows access to references outside of it's context. `GetNext()` is callable, in other words, whereas if left out it might not have a reference to it from that outer scope. Just a guess. – Jared Farrish Jan 16 '20 at 21:45
  • @JaredFarrish Hmm, thanks for the suggestion. I included a couple more examples of with/without from the code's bundled IDE test cases and they both access things from the outer scope. The one without is inside a struct and the one with is in a class, but I don't know if that means anything. – Ruzihm Jan 16 '20 at 21:54
  • 1
    The documentation isn't great. Looking around, the only references to `&` were about references. YMMV. – Jared Farrish Jan 16 '20 at 22:01
  • The difference between the classes includes the first, with the `[&]`, being called `Splattable` (the other simply `ClassA`). Wonder if that relates; the classes are otherwise pretty similar. – Jared Farrish Jan 17 '20 at 14:40

1 Answers1

1

[&] Means capture any referenced locals by reference rather than by value.

In the example, for ClassA this is being passed by value to the lambda. Effectively: this.mA++;

Isaac Paul
  • 1,959
  • 2
  • 15
  • 20
  • That's what I thought at first but then I found the `Class A` example, where it is not used. And in the `Splattable` example, it is used, and it seems to modify `testA` and `testB`, so to me that means it gets a reference and modifies the value it refers to. I notice that `Spattable` is a struct and `ClassA` is a class, though. Not sure if that's relevant – Ruzihm Apr 20 '20 at 13:28
  • 1
    Its is relevant because structs are value types (plain old data) while classes are reference types. – Isaac Paul Apr 20 '20 at 19:50
  • 1
    That makes perfect sense now!! Thank you! – Ruzihm Apr 20 '20 at 20:24