Import a Module =============== {{#include ../../links.md}} ```admonish info.side "See also" See [_Module Resolvers_][module resolver] for more details. ``` Before a [module] can be used (via an `import` statement) in a script, there must be a [module resolver] registered into the [`Engine`], the default being the `FileModuleResolver`. `import` Statement ------------------ ```admonish tip.side.wide "Tip" A [module] that is only `import`-ed but not given any name is simply run. This is a very simple way to run another script file from within a script. ``` A [module] can be _imported_ via the `import` statement, and be given a name. Its members can be accessed via `::` similar to C++. ```js import "crypto_banner"; // run the script file 'crypto_banner.rhai' without creating an imported module import "crypto" as lock; // run the script file 'crypto.rhai' and import it as a module named 'lock' const SECRET_NUMBER = 42; let mod_file = `crypto_${SECRET_NUMBER}`; import mod_file as my_mod; // load the script file "crypto_42.rhai" and import it as a module named 'my_mod' // notice that module path names can be dynamically constructed! // any expression that evaluates to a string is acceptable after the 'import' keyword lock::encrypt(secret); // use functions defined under the module via '::' lock::hash::sha256(key); // sub-modules are also supported print(lock::status); // module variables are constants lock::status = "off"; // <- runtime error: cannot modify a constant ``` ```admonish info "Imports are _scoped_" [Modules] imported via `import` statements are only accessible inside the relevant block scope. ~~~js import "hacker" as h; // import module - visible globally if secured { // <- new block scope let mod = "crypt"; import mod + "o" as c; // import module (the path needs not be a constant string) let x = c::encrypt(key); // use a function in the module h::hack(x); // global module 'h' is visible here } // <- module 'c' disappears at the end of the block scope h::hack(something); // this works as 'h' is visible c::encrypt(something); // <- this causes a run-time error because // module 'c' is no longer available! fn foo(something) { h::hack(something); // <- this also works as 'h' is visible } for x in 0..1000 { import "crypto" as c; // <- importing a module inside a loop is a Very Bad Idea™ c.encrypt(something); } ~~~ ``` ~~~admonish note "Place `import` statements at the top" `import` statements can appear anywhere a normal statement can be, but in the vast majority of cases they are usually grouped at the top (beginning) of a script for manageability and visibility. It is not advised to deviate from this common practice unless there is a _Very Good Reason™_. Especially, do not place an `import` statement within a loop; doing so will repeatedly re-load the same [module] during every iteration of the loop! ~~~ ~~~admonish danger "Recursive imports" Beware of _import cycles_ – i.e. recursively loading the same [module]. This is a sure-fire way to cause a stack overflow in the [`Engine`], unless stopped by setting a limit for [maximum number of modules]. For instance, importing itself always causes an infinite recursion: ```js ┌────────────┐ │ hello.rhai │ └────────────┘ import "hello" as foo; // import itself - infinite recursion! foo::do_something(); ``` [Modules] cross-referencing also cause infinite recursion: ```js ┌────────────┐ │ hello.rhai │ └────────────┘ import "world" as foo; foo::do_something(); ┌────────────┐ │ world.rhai │ └────────────┘ import "hello" as bar; bar::do_something_else(); ``` ~~~