Modules and Libraries - D Programming Language
Description: D Programming Language
Keywords: 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.

# Modules and Libraries

The building blocks of D programs (and libraries) are modules.

D modules are based on a simple concept: Every source file is a module. Accordingly, the single files that we have been writing our programs in have all been individual modules.

By default, the name of a module is the same as its filename without the `.d` extension. When explicitly specified, the name of the module is defined by the `module` keyword, which must appear as the first non-comment line in the source file.

For example, assuming that the name of a source file is "cat.d", the name of the module would be specified by the `module` keyword:

    module cat;

    class Cat {
        // ...
    }

The `module` line is optional if the module is not part of any package (see below). When not specified, it is the same as the file name without the `.d` extension.

###### `static this()` and `static ~this()`

`static this()` and `static ~this()` at module scope are similar to their `struct` and `class` counterparts:

    module cat;

    static this() {
        // ... the initial operations of the module ...
    }

    static ~this() {
        // ... the final operations of the module ...
    }

Code that are in these scopes are executed once for each thread. (Note that most programs consist of a single thread that starts executing the `main()` function.) Code that should be executed only once for the entire program (e.g. initializing `shared` and `immutable` variables) must be defined in `shared static this()` and `shared static ~this()` blocks, which will be covered in the Data Sharing Concurrency chapter.

###### File and module names

D supports Unicode in source code and module names. However, the Unicode support of file systems vary. For example, although most Linux file systems support Unicode, the file names in Windows file systems may not distinguish between lower and upper case letters. Additionally, most file systems limit the characters that can be used in file and directory names.

For portability reasons, I recommend that you use only lower case ASCII letters in file names. For example, "resume.d" would be a suitable file name for a class named `Résumé`.

Accordingly, the name of the module would consist of ASCII letters as well:

    module resume;  // Module name consisting of ASCII letters

    class Résumé {  // Program code consisting of Unicode characters
        // ...
    }

##### Packages

A combination of related modules are called a package. D packages are a simple concept as well: The source files that are inside the same directory are considered to belong to the same package. The name of the directory becomes the name of the package, which must also be specified as the first parts of module names.

For example, if "cat.d" and "dog.d" are inside the directory "animal", then specifying the directory name along with the module name makes them be a part of the same package:

    module animal.cat;

    class Cat {
        // ...
    }

Similarly, for the `dog` module:

    module animal.dog;

    class Dog {
        // ...
    }

For modules that are parts of packages, the `module` line is not optional and the whole module name including the package name must be specified.

Since package names correspond to directory names, the package names of modules that are deeper than one directory level must reflect that hierarchy. For example, if the "animal" directory included a "vertebrate" directory, the name of a module inside that directory would include `vertebrate` as well:

    module animal.vertebrate.cat;

The directory hierarchies can be arbitrarily complex depending on the needs of the program. Relatively short programs usually have all of their source files in a single directory.

##### Importing modules

The `import` keyword, which we have been using in almost every program so far, is for introducing a module to the current module:

    import std.stdio;

The module name may contain the package name as well. For example, the `std.` part above indicates that `stdio` is a module that is a part of the `std` package.

The `animal.cat` and `animal.dog` modules would be imported similarly. Let's assume that the following code is inside a file named "deneme.d":

    module deneme;        // the name of this module

    import animal.cat;    // a module that it uses
    import animal.dog;    // another module that it uses

    void main() {
        auto cat = new Cat();
        auto dog = new Dog();
    }

Note: As described below, for the program to be built correctly, those module files must also be provided to the linker.

More than one module can be imported at the same time:

    import animal.cat, animal.dog;

###### Selective imports

Instead of importing a module as a whole with all of its names, it is possible to import just specific names from it.

    import std.stdio : writeln;

    // ...

        writefln("Hello %s.", name);    // ← derleme HATASI

The code above cannot be compiled because only `writeln` is imported, not `writefln`.

Selective imports are considered to be better than importing an entire module because it reduces the chance of name collisions. As we will see in an example below, a name collision can occur when the same name appears in more than one imported module.

Selective imports may reduce compilation times as well because the compiler needs to compile only the parts of a module that are actually imported. On the other hand, selective imports require more work as every imported name must be specified separately on the `import` line.

This book does not take advantage of selective imports mostly for brevity.

###### Local imports

So far we have always imported all of the required modules at the tops of programs:

    import std.stdio;     // ← at the top
    import std.string;    // ← at the top

    // ... the rest of the module ...

Instead, modules can be imported at any other line of the source code. For example, the two functions of the following program import the modules that they need in their own scopes:

    string makeGreeting(string name) {
        import std.string;

        string greeting = format("Hello %s", name);
        return greeting;
    }

    void interactWithUser() {
        import std.stdio;

        write("Please enter your name: ");
        string name = readln();
        writeln(makeGreeting(name));
    }

    void main() {
        interactWithUser();
    }

Local imports are recommended over global imports because instead of importing every module unconditionally at the top, the compiler can import only the ones that are in the scopes that are actually used. If the compiler knows that the program never calls a function, it can ignore the import directives inside that function.

Additionally, a locally imported module is accessible only inside that local scope, further reducing the risk of name collisions.

We will later see in the Mixins chapter that local imports are in fact required for template mixins.

The examples throughout this book do not take advantage of local imports mostly because local imports were added to D after the start of writing this book.

###### Locations of modules

The compiler finds the module files by converting the package and module names directly to directory and file names.

For example, the previous two modules would be located as "animal/cat.d" and "animal/dog.d", respectively (or "animal\cat.d" and "animal\dog.d", depending on the file system). Considering the main source file as well, the program above consists of three files.

###### Long and short module names

The names that are used in the program may be spelled out with the module and package names:

        auto cat0 = Cat();
        auto cat1 = animal.cat.Cat();   // same as above

The long names are normally not needed but sometimes there are name conflicts. For example, when referring to a name that appears in more than one module, the compiler cannot decide which one is meant.

The following program is spelling out the long names to distinguish between two separate `Jaguar` structs that are defined in two separate modules: `animal` and `car`:

    import animal.jaguar;
    import car.jaguar;

    // ...

        auto conflicted =  Jaguar();            // ← derleme HATASI

        auto myAnimal = animal.jaguar.Jaguar(); // ← compiles
        auto myCar    =    car.jaguar.Jaguar(); // ← compiles

###### Renamed imports

It is possible to rename imported modules either for convenience or to resolve name conflicts:

    import carnivore = animal.jaguar;
    import vehicle = car.jaguar;

    // ...

        auto myAnimal = carnivore.Jaguar();       // ← compiles
        auto myCar    = vehicle.Jaguar();         // ← compiles

Instead of renaming the entire import, it is possible to rename individual imported symbols.

For example, when the following code is compiled with the `-w` compiler switch, the compiler would warn that `sort()` function should be preferred instead of `.sort` property:

    import std.stdio;
    import std.algorithm;

    // ...

        auto arr = [ 2, 10, 1, 5 ];
        arr.sort;    // ← compilation WARNING
        writeln(arr);

    Warning: use std.algorithm.sort instead of .sort property

Note: The `arr.sort` expression above is the equivalent of `sort(arr)` but it is written in the UFCS syntax, which we will see in a later chapter.

One solution in this case is to import `std.algorithm.sort` by renaming it. The new name `algSort` below means the `sort()` function and the compiler warning is eliminated:

    import std.stdio;
    import std.algorithm : algSort = sort;

    void main() {
        auto arr = [ 2, 10, 1, 5 ];
        arr.algSort;
        writeln(arr);
    }

###### Importing a package as a module

Sometimes multiple modules of a package may need to be imported together. For example, whenever one module from the `animal` package is imported, all of the other modules may need to be imported as well: `animal.cat`, `animal.dog`, `animal.horse`, etc.

In such cases it is possible to import some or all of the modules of a package by importing the package as if it were a module:

    import animal;    // ← entire package imported as a module

It is achieved by a special configuration file in the package directory, which must always be named as `package.d`. That special file includes the `module` directive for the package and imports the modules of the package publicly:

    // The contents of the file animal/package.d:
    module animal;

    public import animal.cat;
    public import animal.dog;
    public import animal.horse;
    // ... same for the other modules ...

Importing a module publicly makes that module available to the users of the importing module as well. As a result, when the users import just the `animal` module (which actually is a package), they get access to `animal.cat` and all the other modules as well.

###### Deprecating features

Modules evolve over time and get released under new version numbers. Going forward from a particular version, the authors of the module may decide to deprecate some of its features. Deprecating a feature means that newly written programs should not rely on that feature anymore; using a deprecated feature is disapproved. Deprecated features may even be removed from the module in the future.

There can be many reasons why a feature is deprecated. For example, the new version of the module may include a better alternative, the feature may have been moved to another module, the name of the feature may have changed to be consistent with the rest of the module, etc.

The deprecation of a feature is made official by defining it with the `deprecated` attribute, optionally with a custom message. For example, the following deprecation message communicates to its user that the name of the function has been changed:

    deprecated("Please use doSomething() instead.")
    void do_something() {
        // ...
    }

By specifying one of the following compiler switches, the user of the module can determine how the compiler should react when a deprecated feature is used:

- `-d`: Using deprecated features should be allowed
- `-dw`: Using deprecated features should produce compilation warnings
- `-de`: Using deprecated features should produce compilation errors

For example, calling the deprecated feature in a program and compiling it with `-de` would fail compilation:

        do_something();

    $ dmd deneme.d -de
    deneme.d: Deprecation: function deneme.do_something is
    deprecated - Please use doSomething() instead.

The name of a deprecated feature is usually defined as an `alias` of the new name:

    deprecated("Please use doSomething() instead.")
    alias do_something = doSomething;

    void doSomething() {
        // ...
    }

We will see the `alias` keyword in a later chapter.

###### Adding module definitions to the program

The `import` keyword is not sufficient to make modules become parts of the program. It simply makes available the features of a module inside the current module. That much is needed only to compile the code.

It is not possible to build the previous program only by the main source file, "deneme.d":

    $ dmd deneme.d -w -de
    deneme.o: In function `_Dmain':
    deneme.d: undefined reference to `_D6animal3cat3Cat7__ClassZ'
    deneme.d: undefined reference to `_D6animal3dog3Dog7__ClassZ'
    collect2: ld returned 1 exit status

Those error messages are generated by the linker. Although they are not user-friendly messages, they indicate that some definitions that are needed by the program are missing.

The actual build of the program is the responsibility of the linker, which gets called automatically by the compiler behind the scenes. The compiler passes the modules that it has just compiled to the linker, and the linker combines those modules (and libraries) to produce the executable program.

For that reason, all of the modules that make up the program must be provided to the linker. For the program above to be built, "animal/cat.d" and "animal/dog.d" must also be specified on the compilation line:

    $ dmd deneme.d animal/cat.d animal/dog.d -w -de

Instead of having to mention the modules individually every time on the command line, they can be combined as libraries.

##### Libraries

A collection of compiled modules is called a library. Libraries are not programs themselves; they do not have the `main()` function. Libraries contain compiled definitions of functions, structs, classes, and other features of modules, which are to be linked later by the linker to produce the program.

dmd's `-lib` command line option is for making libraries. The following command makes a library that contains the "cat.d" and the "dog.d" modules. The name of the library is specified with the `-of` switch:

    $ dmd animal/cat.d animal/dog.d -lib -ofanimal -w -de

The actual name of the library file depends on the platform. For example, the extension of library files is `.a` under Linux systems: `animal.a`.

Once that library is built, It is not necessary to specify the "animal/cat.d" and "animal/dog.d" modules individually anymore. The library file is sufficient:

    $ dmd deneme.d animal.a -w -de

The command above replaces the following one:

    $ dmd deneme.d animal/cat.d animal/dog.d -w -de

As an exception, the D standard library Phobos need not be specified on the command line. That library is automatically included behind the scenes. Otherwise, it could be specified similar to the following line:

    $ dmd deneme.d animal.a /usr/lib64/libphobos2.a -w -de

Note: The name and location of the Phobos library may be different on different systems.

###### Using libraries of other languages

D can use libraries that are written in some other compiled languages like C and C++. However, because different languages use different linkages, such libraries are available to D code only through their D bindings.

Linkage is the set of rules that determines the accessibility of entities in a library as well as how the names (symbols) of those entities are represented in compiled code. The names in compiled code are different from the names that the programmer writes in source code: The compiled names are name-mangled according to the rules of a particular language or compiler.

For example, according to C linkage, the C function name `foo` would be mangled with a leading underscore as `_foo` in compiled code. Name-mangling is more complex in languages like C++ and D because these languages allow using the same name for different entities in different modules, structs, classes, etc. as well as for overloads of functions. A D function named `foo` in source code has to be mangled in a way that would differentiate it from all other `foo` names that can exist in a program. Although the exact mangled names are usually not important to the programmer, the `core.demangle` module can be used to mangle and demangle symbols:

    module deneme;

    import std.stdio;
    import core.demangle;

    void foo() {
    }

    void main() {
        writeln(mangle!(typeof(foo))("deneme.foo"));
    }

Note: `mangle()` is a function template, the syntax of which is unfamiliar at this point in the book. We will see templates later in the Templates chapter.

A function that has the same type as `foo` above and is named as `deneme.foo`, has the following mangled name in compiled code:

    _D6deneme3fooFZv

Name mangling is the reason why linker error messages cannot include user-friendly names. For example, a symbol in an error message above was `_D6animal3cat3Cat7__ClassZ` instead of `animal.cat.Cat`.

The `extern()` attribute specifies the linkage of entities. The valid linkage types that can be used with `extern()` are `C`, `C++`, `D`, `Objective-C`, `Pascal`, `System`, and `Windows`. For example, when a D code needs to make a call to a function that is defined in a C library, that function must be declared as having C linkage:

    // Declaring that 'foo' has C linkage (e.g. it may be defined
    // in a C library):
    extern(C) void foo();

    void main() {
        foo();  // this call would be compiled as a call to '_foo'
    }

In the case of C++ linkage, the namespace that a name is defined in is specified as the second argument to the `extern()` attribute. For example, according to the following declaration, `bar()` is the declaration of the function `a::b::c::bar()` defined in a C++ library (note that D code uses dots instead of colons):

    // Declaring that 'bar' is defined inside namespace a::b::c
    // and that it has C++ linkage:
    extern(C++, a.b.c) void bar();

    void main() {
        bar();          // a call to a::b::c::bar()
        a.b.c.bar();    // same as above
    }

A file that contains such D declarations of the features of an external library is called a D binding of that library. Fortunately, in most cases programmers do not need to write them from scratch as D bindings for many popular non-D libraries are available through the Deimos project.

When used without a linkage type, the `extern` attribute has a different meaning: It specifies that the storage for a variable is the responsibility of an external library; the D compiler should not reserve space for it in this module. Having different meanings, `extern` and `extern()` can be used together:

    // Declaring that the storage for 'g_variable' is already
    // defined in a C library:
    extern(C) extern int g_variable;

If the `extern` attribute were not specified above, while having C linkage, `g_variable` would be a variable of this D module.

Copyright © 1999-2026 by the D Language Foundation | Page generated by Ddoc on Wed Aug 5 11:24:17 2026