Nested functions, structs, and classes - D Programming Language

Menu

- Learn
- Documentation
  - Language Reference
  - Library Reference
  - Command-line Reference
  - Feature Overview
  - Articles
- Downloads
- Packages
- Community
  - Blog
  - Orgs using D
  - Twitter
  - Calendar
  - Discord (community-run)
  - Forums
  - IRC
  - Wiki
  - Issues
  - Contributing
  - Contributors
  - Foundation
  - Security Team
  - Donate
  - Sponsors
- Resources
  - Tour
  - Books
  - Tutorials
  - Tools
  - Editors
  - IDEs
  - run.dlang.io
  - Visual D
  - Acknowledgments
  - D Style
  - Glossary
  - Sitemap

Search
Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

# Nested functions, structs, and classes

Up to this point, we have been defining functions, structs, and classes in the outermost scopes (i.e. the module scope). They can be defined in inner scopes as well. Defining them in inner scopes helps with encapsulation by narrowing the visibility of their symbols, as well as creating closures that we saw in the Function Pointers, Delegates, and Lambdas chapter.

As an example, the following `outerFunc()` function contains definitions of a nested function, a nested `struct`, and a nested `class`:

    void outerFunc(int parameter) {
        int local;

        void nestedFunc() {
            local = parameter * 2;
        }

        struct NestedStruct {
            void memberFunc() {
                local /= parameter;
            }
        }

        class NestedClass {
            void memberFunc() {
                local += parameter;
            }
        }

        // Using the nested definitions inside this scope:

        nestedFunc();

        auto s = NestedStruct();
        s.memberFunc();

        auto c = new NestedClass();
        c.memberFunc();
    }

    void main() {
        outerFunc(42);
    }

Like any other variable, nested definitions can access symbols that are defined in their outer scopes. For example, all three of the nested definitions above are able to use the variables named `parameter` and `local`.

As usual, the names of the nested definitions are valid only in the scopes that they are defined in. For example, `nestedFunc()`, `NestedStruct`, and `NestedClass` are not accessible from `main()`:

    void main() {
        auto a = NestedStruct();              // ← derleme HATASI
        auto b = outerFunc.NestedStruct();    // ← derleme HATASI
    }

Although their names cannot be accessed, nested definitions can still be used in other scopes. For example, many Phobos algorithms handle their tasks by nested structs that are defined inside Phobos functions.

To see an example of this, let's design a function that consumes a slice from both ends in alternating order:

    import std.stdio;
    import std.array;

    auto alternatingEnds(T)(T[] slice) {
        bool isFromFront = true;

        struct EndAlternatingRange {
            bool empty() const {
                return slice.empty;
            }

            T front() const {
                return isFromFront ? slice.front : slice.back;
            }

            void popFront() {
                if (isFromFront) {
                    slice.popFront();
                    isFromFront = false;

                } else {
                    slice.popBack();
                    isFromFront = true;
                }
            }
        }

        return EndAlternatingRange();
    }

    void main() {
        auto a = alternatingEnds([ 1, 2, 3, 4, 5 ]);
        writeln(a);
    }

Even though the nested `struct` cannot be named inside `main()`, it is still usable:

    [1, 5, 2, 4, 3]

Note: Because their names cannot be mentioned outside of their scopes, such types are called Voldemort types due to analogy to a Harry Potter character.

Note that the nested `struct` that `alternatingEnds()` returns does not have any member variables. That `struct` handles its task using merely the function parameter `slice` and the local function variable `isFromFront`. The fact that the returned object can safely use those variables even after leaving the context that it was created in is due to a closure that has been created automatically. We have seen closures in the Function Pointers, Delegates, and Lambdas chapter.

###### `static` when a closure is not needed

Since they keep their contexts alive, nested definitions are more expensive than their regular counterparts. Additionally, as they must include a context pointer to determine the context that they are associated with, objects of nested definitions occupy more space as well. For example, although the following two structs have exactly the same member variables, their sizes are different:

    import std.stdio;

    struct ModuleStruct {
        int i;

        void memberFunc() {
        }
    }

    void moduleFunc() {
        struct NestedStruct {
            int i;

            void memberFunc() {
            }
        }

        writefln("OuterStruct: %s bytes, NestedStruct: %s bytes.",
                 ModuleStruct.sizeof, NestedStruct.sizeof);
    }

    void main() {
        moduleFunc();
    }

The sizes of the two structs may be different on other environments:

    OuterStruct: 4 bytes, NestedStruct: 16 bytes.

However, some nested definitions are merely for keeping them as local as possible, with no need to access variables from the outer contexts. In such cases, the associated cost would be unnecessary. The `static` keyword removes the context pointer from nested definitions, making them equivalents of their module counterparts. As a result, `static` nested definitions cannot access their outer contexts:

    void outerFunc(int parameter) {
        static class NestedClass {
            int i;

            this() {
                i = parameter;    // ← derleme HATASI
            }
        }
    }

The context pointer of a nested `class` object is available as a `void*` through its `.outer` property. For example, because they are defined in the same scope, the context pointers of the following two objects are equal:

    void foo() {
        class C {
        }

        auto a = new C();
        auto b = new C();

        assert(a.outer is b.outer);
    }

As we will see below, for classes nested inside classes, the type of the context pointer is the type of the outer class, not `void*`.

###### Classes nested inside classes

When a `class` is nested inside another one, the context that the nested object is associated with is the outer object itself.

Such nested classes are constructed by the `this.new` syntax. When necessary, the outer object of a nested object can be accessed by `this.outer`:

    class OuterClass {
        int outerMember;

        class NestedClass {
            int func() {
                /* A nested class can access members of the outer
                 * class. */
                return outerMember * 2;
            }

            OuterClass context() {
                /* A nested class can access its outer object
                 * (i.e. its context) by '.outer'. */
                return this.outer;
            }
        }

        NestedClass algorithm() {
            /* An outer class can construct a nested object by
             * '.new'. */
            return this.new NestedClass();
        }
    }

    void main() {
        auto outerObject = new OuterClass();

        /* A member function of an outer class is returning a
         * nested object: */
        auto nestedObject = outerObject.algorithm();

        /* The nested object gets used in the program: */
        nestedObject.func();

        /* Naturally, the context of nestedObject is the same as
         * outerObject: */
        assert(nestedObject.context() is outerObject);
    }

Instead of `this.new` and `this.outer`, `.new` and `.outer` can be used on existing objects as well:

        auto var = new OuterClass();
        auto nestedObject = var.new OuterClass.NestedClass();
        auto var2 = nestedObject.outer;

##### Summary

- Functions, structs, and classes that are defined in inner scopes can access those scopes as their contexts.
- Nested definitions keep their contexts alive to form closures.
- Nested definitions are more costly than their module counterparts. When a nested definition does not need to access its context, this cost can be avoided by the `static` keyword.
- Classes can be nested inside other classes. The context of such a nested object is the outer object itself. Nested class objects are constructed by `this.new` or `variable.new` and their contexts are available by `this.outer` or `variable.outer`.

Copyright © 1999-2026 by the D Language Foundation | Page generated by Ddoc on Mon Aug 17 18:57:11 2026