Remix.run Logo
WalterBright 42 minutes ago

I use them all the time. It's one of the nicest and cleanest features of D. It's an elegant way of:

1. grouping together strongly related functions that are implicitly private to the enclosing function

2. obviating the need to create a struct in order to pass common context to multiple functions

For an example, here's a tree walking function that uses a nested function for the recursion:

    private void unrollWalker(elem* e, uint defnum, Symbol* v, targ_llong increment, int unrolls) nothrow
    {
        int state = 0;

        /***********************************
         * Walk e in execution order, fixing it according to state.
         * state == 0..unrolls-1: when eincrement is found, remove it, advance to next state
         * state == 1..unrolls-1: replacing instances of v with v+(state*increment),
         * state == unrolls-1: leave eincrement alone, advance to next state
         * state == unrolls: done
         */

        void walker(elem* e) @trusted
        {
            assert(e);
            const op = e.Eoper;
            if (ERTOL(e))
            {
                if (e.Edef != defnum)
                {
                    walker(e.E2); // this function is @trusted because of this union access
                    walker(e.E1);
                }
            }
            else if (OTbinary(op))
            {
                if (e.Edef != defnum)
                {
                    walker(e.E1);
                    walker(e.E2);
                }
            }
            else if (OTunary(op))
            {
                assert(e.Edef != defnum);
                walker(e.E1);
            }
            else if (op == OPvar &&
                     state &&
                     e.Vsym == v)
            {
                // overwrite e with (v+increment)
                elem* e1 = el_calloc();
                el_copy(e1,e);
                e.Eoper = OPadd;
                e.E1 = e1;
                e.E2 = el_long(e.Ety, increment * state);
            }
            if (OTdef(op) && e.Edef == defnum)
            {
                // found the increment elem; neuter all but the last one
                if (state + 1 < unrolls)
                {
                    el_free(e.E1);
                    el_free(e.E2);
                    e.Eoper = OPconst;
                    e.Vllong = 0;
                }
                ++state;
            }
        }

        walker(e);
        assert(state == unrolls);
    }
Only one argument needs to be passed to walker(), because the other context data is accessible from the enclosing function.

https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ba...