Namespaces, globals and packages
Namespaces
Section titled “Namespaces”namespace a { ... } groups declarations; a::name reaches them. Namespaces nest (namespace a::b is short for one inside the other), and the same namespace can be opened again in another
place or file.
use std::io;
namespace geo { struct vec2 { x: f64; y: f64; }
fn len2(v: vec2) -> f64 { return v.x * v.x + v.y * v.y; }
namespace units { fn to_cm(m: f64) -> f64 { return m * 100.0; } }}
fn main() -> void { val v: geo::vec2 = { x: 3.0, y: 4.0 }; std::println("{} {}", geo::len2(v), geo::units::to_cm(1.23));}// expect: 25 123Inside a namespace, its own names (and those of the namespaces around it) are reachable without a prefix.
use a::b::c; makes c’s members reachable through the first namespace of the path, a: so
after use std::io;, std::io::println can be written std::println. When c is a single item
instead of a namespace, the item itself becomes a::c. The full path keeps working.
use std::io;use app::util;
namespace app::util { fn twice(x: i32) -> i32 { return x * 2; }}
fn main() -> void { std::println("{}", app::twice(21)); std::io::println("full paths still work");}// expect: 42// expect: full paths still workTwo uses that bring the same name into one namespace merge into one overload set. A method the
namespace attaches doesn’t hide such a name either, since methods are called as x.name(), never
by path: std attaches min to slices, and after use std::math;, std::min(a, b) is math’s
function.
use { "header.h" } as c; is different: it imports a C header into namespace c. See
C interop.
Globals
Section titled “Globals”val and var work at the top level and in namespaces. A global’s value must be known at compile
time: arithmetic and bit operations over constants and other val globals work (1 << 13,
FLAG | 0x10), and so do calls to comptime functions. The result must fit the global’s type. A
literal of constants works too (an array, a struct, an enum variant with its payload, an
optional), and so does another global’s address (val at: i32* = &hits;).
use std::io;
val LIMIT: i32 = 3 * 4;var hits: i32 = 0;
fn record() -> void { hits += 1;}
fn main() -> void { record(); record(); std::println("{} {}", LIMIT, hits);}// expect: 12 2A static local keeps its value between calls: static var calls: i32 = 0; inside a function.
@attributes([@thread_local]) on a global var gives each thread its own copy (see
Attributes).
Visibility
Section titled “Visibility”A package’s declarations are its own unless it marks them public: code in the package can use
any of them, and anywhere else, naming one that isn’t public (a function, method, type or global) is
a compile error, 'helper' isn't public in package mylib. So a package’s API is what it says is.
public fn api() -> i32 { return helper() + 1; } // the package's API
fn helper() -> i32 { return 1; } // its ownA program’s own files never need public: everything they declare is visible, to std’s templates
too (internal still keeps a declaration to the program). A struct’s fields and an enum’s variants
go with their type (internal on a field hides it outside its package: naming it in source there, to read, write or set it in a literal, is the error, while reflection and @derive see every field), an export fn is
public, and so are the functions in an attach some_trait -> T { } block, which are the trait’s.
Templates are fine: a std template that calls one of std’s own helpers still works when your
program instantiates it.
Packages
Section titled “Packages”A program’s own files (every file on the command line) share one top level, so each sees what the
others declare. A package is a directory of .volt files, and each of its files is wrapped in
namespace NAME. That’s all std is: every std file is inside namespace std. You give voltc
packages with --pkg NAME=DIR (bolt does it for dependencies), and the program reaches them as
NAME::.... See Packages and std.