Module:
ModuleDeclaration DeclDefs
DeclDefs
DeclDefs:
DeclDef
DeclDef DeclDefs
DeclDef:
AttributeSpecifier
Declaration
Constructor
Destructor
Postblit
Allocator
Deallocator
ClassInvariant
StructInvariant
UnitTest
AliasThis
StaticConstructor
StaticDestructor
SharedStaticConstructor
SharedStaticDestructor
ConditionalDeclaration
DebugSpecification
VersionSpecification
StaticAssert
TemplateDeclaration
TemplateMixinDeclaration
TemplateMixin
MixinDeclaration
;
Modules have a one-to-one correspondence with source files. The module name is, by default, the file name with the path and extension stripped off, and can be set explicitly with the module declaration.
Modules automatically provide a namespace scope for their contents. Modules superficially resemble classes, but differ in that:
Modules can be grouped together in hierarchies called packages.
Modules offer several guarantees:
The ModuleDeclaration sets the name of the module and what package it belongs to. If absent, the module name is taken to be the same name (stripped of path and extension) of the source file name.
ModuleDeclaration:
ModuleAttributesopt module ModuleFullyQualifiedName ;
ModuleAttributes:
ModuleAttribute
ModuleAttribute ModuleAttributes
ModuleAttribute:
DeprecatedAttribute
UserDefinedAttribute
ModuleFullyQualifiedName:
ModuleName
Packages . ModuleName
ModuleName:
Identifier
Packages:
PackageName
Packages . PackageName
PackageName:
Identifier
The Identifiers preceding the rightmost are the Packages that the module is in. The packages correspond to directory names in the source file path. Package and module names cannot be Keywords.
If present, the ModuleDeclaration appears syntactically first in the source file, and there can be only one per source file.
Example:
module c.stdio; // module stdio in the c package
By convention, package and module names are all lower case. This is because those names can have a one-to-one correspondence with the operating system's directory and file names, and many file systems are not case sensitive. All lower case package and module names will minimize problems moving projects between dissimilar file systems.
If the file name of a module is an invalid module name (e.g. foo-bar.d), you may use a module declaration to set a valid module name:
module foo_bar;
ModuleDeclaration can have an optional DeprecatedAttribute. The compiler will produce a message when the deprecated module is imported.
deprecated module foo;
module bar; import foo; // Deprecated: module foo is deprecated
DeprecatedAttribute can have an optional AssignExpression argument to provide a more expressive message. The AssignExpression must evaluate at compile time to a string.
deprecated("Please use foo2 instead.")
module foo;
module bar; import foo; // Deprecated: module foo is deprecated - Please use foo2 instead.Implementation Defined:
_ to ensure maximum portability and compatibility with various file systems._ and should not be a Keyword.Symbols from one module are made available in another module by using the ImportDeclaration:
ImportDeclaration:
import ImportList ;
static import ImportList ;
ImportList:
Import
ImportBindings
Import , ImportList
Import:
ModuleFullyQualifiedName
ModuleAliasIdentifier = ModuleFullyQualifiedName
ImportBindings:
Import : ImportBindList
ImportBindList:
ImportBind
ImportBind , ImportBindList
ImportBind:
Identifier
Identifier = Identifier
ModuleAliasIdentifier:
Identifier
There are several forms of the ImportDeclaration, from generalized to fine-grained importing.
The order in which ImportDeclarations occur has no significance.
ModuleFullyQualifiedNames in the ImportDeclaration must be fully qualified with whatever packages they are in. They are not considered to be relative to the module that imports them.
The simplest form of importing is to just list the modules being imported:
module myapp.main;
import std.stdio; // import module stdio from package std
class Foo : BaseClass
{
import myapp.foo; // import module myapp.foo in this class' scope
void bar ()
{
import myapp.bar; // import module myapp.bar in this function' scope
writeln("hello!"); // calls std.stdio.writeln
}
}
When a symbol name is used unqualified, a two-phase lookup will happen. First, the module scope will be searched, starting from the innermost scope. For example, in the previous example, while looking for writeln, the order will be:
bar.Foo.BaseClass.If the first lookup wasn't successful, a second one is performed on imports. In the second lookup phase inherited scopes are ignored. This includes scope of base classes and interface (in this example, BaseClass's imports would be ignored), as well as imports in mixed-in template.
Symbol lookup stops as soon as a symbol is found. If two symbols with the same name are found at the same lookup phase, this ambiguity will result in a compilation error.
module A; void foo(); void bar();
module B; void foo(); void bar();
module C;
import A;
void foo();
void test()
{
foo(); // C.foo() is called, it is found before imports are searched
bar(); // A.bar() is called, since imports are searched
}
module D;
import A;
import B;
void test()
{
foo(); // error, A.foo() or B.foo() ?
A.foo(); // ok, call A.foo()
B.foo(); // ok, call B.foo()
}
module E;
import A;
import B;
alias foo = B.foo;
void test()
{
foo(); // call B.foo()
A.foo(); // call A.foo()
B.foo(); // call B.foo()
}
By default, imports are private. This means that if module A imports module B, and module B imports module C, then names from C are visible only from B and not from A.
An import can be explicitly declared public, which will cause names from the imported module to be visible to further imports. So in the above example where module A imports module B, if module B publicly imports module C, names from C will be visible in A as well.
All symbols from a publicly imported module are also aliased in the importing module. Thus in the above example if C contains the name foo, it will be accessible in A as foo, B.foo and C.foo.
For another example:
module W;
void foo() { }
module X;
void bar() { }
module Y; import W; public import X; ... foo(); // calls W.foo() bar(); // calls X.bar()
module Z; import Y; ... foo(); // error, foo() is undefined bar(); // ok, calls X.bar() X.bar(); // ditto Y.bar(); // ok, Y.bar() is an alias to X.bar()
static import requires use of a fully qualified name to reference the module's names:
static import std.stdio;
void main()
{
writeln("hello!"); // error, writeln is undefined
std.stdio.writeln("hello!"); // ok, writeln is fully qualified
}
A local name for an import can be given, through which all references to the module's symbols must be qualified with:
import io = std.stdio;
void main()
{
io.writeln("hello!"); // ok, calls std.stdio.writeln
std.stdio.writeln("hello!"); // error, std is undefined
writeln("hello!"); // error, writeln is undefined
}
Best Practices: Renamed imports are handy when dealing with very long import names. Specific symbols can be exclusively imported from a module and bound into the current namespace:
import std.stdio : writeln, foo = write;
void main()
{
std.stdio.writeln("hello!"); // error, std is undefined
writeln("hello!"); // ok, writeln bound into current namespace
write("world"); // error, write is undefined
foo("world"); // ok, calls std.stdio.write()
fwritefln(stdout, "abc"); // error, fwritefln undefined
}
static cannot be used with selective imports.
When renaming and selective importing are combined:
import io = std.stdio : foo = writeln;
void main()
{
writeln("bar"); // error, writeln is undefined
std.stdio.foo("bar"); // error, foo is bound into current namespace
std.stdio.writeln("bar"); // error, std is undefined
foo("bar"); // ok, foo is bound into current namespace,
// FQN not required
io.writeln("bar"); // ok, io=std.stdio bound the name io in
// the current namespace to refer to the entire
// module
io.foo("bar"); // error, foo is bound into current namespace,
// foo is not a member of io
}
Import declarations may be used at any scope. For example:
void main()
{
import std.stdio;
writeln("bar");
}
The imports are looked up to satisfy any unresolved symbols at that scope. Imported symbols may hide symbols from outer scopes.
In function scopes, imported symbols only become visible after the import declaration lexically appears in the function body. In other words, imported symbols at function scope cannot be forward referenced.
void main()
{
void writeln(string) {}
void foo()
{
writeln("bar"); // calls main.writeln
import std.stdio;
writeln("bar"); // calls std.stdio.writeln
void writeln(string) {}
writeln("bar"); // calls main.foo.writeln
}
writeln("bar"); // calls main.writeln
std.stdio.writeln("bar"); // error, std is undefined
}
A leading . causes the identifer to be looked up in the module scope.
int x;
int foo(int x)
{
if (y)
return x; // returns foo.x, not global x
else
return .x; // returns global x
}
Static constructors are executed to initialize a module's state. Static destructors terminate a module's state.
There can be multiple static constructors and static destructors within one module. The static constructors are run in lexical order, the static destructors are run in reverse lexical order.
Non-shared static constructors and destructors are run whenever threads are created or destroyed, including the main thread.
Shared static constructors are run once on before main() is called. Shared static destructors are run after the main() returns.
import resource;
Resource x;
shared Resource y;
__gshared Resource z;
static this() // non-shared static constructor
{
x = acquireResource();
}
shared static this() // shared static constructor
{
y = acquireSharedResource();
z = acquireSharedResource();
}
static ~this() // non-shared static destructor
{
releaseResource(x);
}
shared static ~this() // shared static destructor
{
releaseSharedResource(y);
releaseSharedResource(z);
}
Best Practices: Shared static constructors on all modules are run before any non-shared static constructors.
The order of static initialization is implicitly determined by the import declarations in each module. Each module is assumed to depend on any imported modules being statically constructed first. There is no other imposed order on executing the module static constructors.
Cycles (circular dependencies) in the import declarations are allowed as long as not both of the modules contain static constructors or static destructors. Violation of this rule will result in a runtime exception.
Implementation Defined:--DRT-oncycle=... where the following behaviors are supported: abort The default behavior. The normal behavior as described in the previous sectiondeprecate This functions just like abort, but upon cycle detection the runtime will use a flawed pre-2.072 algorithm to determine if the cycle was previously detected. If no cycles are detected in the old algorithm, execution continues, but a deprecation message is printed.print Print all cycles detected, but do not abort execution. When cycles are present, order of static construction is implementation defined, and not guaranteed to be valid.ignore Do not abort execution or print any cycles. When cycles are present, order of static construction is implementation defined, and not guaranteed to be valid.Within a module, the static construction occurs in the lexical order in which they appear.
It is defined to be exactly the reverse order that static construction was performed in. Static destructors for individual modules will only be run if the corresponding static constructor successfully completed.
Shared static destructors are executed after static destructors.
Unit tests are run in the lexical order in which they appear within a module.
MixinDeclaration:
mixin ( ArgumentList ) ;
Each AssignExpression in the ArgumentList is evaluated at compile time, and the result must be representable as a string. The resulting strings are concatenated to form a string. The text contents of the string must be compilable as a valid DeclDefs, and is compiled as such.
A package module can be used to publicly import other modules, while enabling a simpler import syntax. It enables converting a module into a package of modules, without breaking existing code which uses that module. Example of a set of library modules:
libweb/client.d:module libweb.client;
void runClient() { }
libweb/server.d: module libweb.server;
void runServer() { }
libweb/package.d: module libweb; public import libweb.client; public import libweb.server;
The package module must have the file name package.d. The module name is declared to be the fully qualified name of the package. Package modules can be imported just like any other modules:
module test;
// import the package module
import libweb;
void main()
{
runClient();
runServer();
}
A package module can be nested inside of a sub-package:
libweb/utils/package.d:// must be declared as the fully qualified name of the package, not just 'utils' module libweb.utils; // publicly import modules from within the 'libweb.utils' package. public import libweb.utils.conv; public import libweb.utils.text;
The package module can then be imported with the standard module import declaration:
test.d:module test;
// import the package module
import libweb.utils;
void main() { }
© 1999–2019 The D Language Foundation
Licensed under the Boost License 1.0.
https://dlang.org/spec/module.html