Items and imports
Everything in Rune has a unique name. Every function, type, and import. This name is what identifies that thing, and is called its item. Rune performs compile time checks to make sure that every item we try to use actually exists.
The following are examples of items in Rune:
std::result::Result
(a type)std::iter::range
(a function)
The first refers to the Result
enum, and the second is the range
function.
They both live within their corresponding std
module. Result
is a bit
special even, since it's part of the prelude, allowing us to use it without
importing it. But what about range
?
If we wanted to use range
we would have to import it first with a use
statement:
use std::iter::once;
let it = once(0);
dbg!(it.next());
dbg!(it.next());
$> cargo run -- run scripts/book/items_imports/example_import.rn
std::iter::Range
Trying to use an item which doesn't exist results in a compile error:
pub fn main() {
let foo = Foo::new();
}
$> cargo run -- run scripts/book/items_imports/missing_item.rn.fail
error: compile error
┌─ scripts/book/items_imports/missing_item.rn.fail:2:15
│
2 │ let foo = Foo::new();
│ ^^^^^^^^ missing item `Foo::new`
Every item used in a Rune program must be known at compile time. This is one of the static guarantees every Rune script are has to fulfill. And is one important point where it differs from Lua or Python.
Modules
Rune has support for modules purely defined in Rune itself. This is done using
the mod
keyword. And the module can either be loaded from a different file
matching the name of the module or defined directly inside of the source file.
The following is an example of an inline module:
mod foo {
pub fn number() {
1
}
}
mod bar {
pub fn number() {
2
}
}
pub fn main() {
dbg!(foo::number() + bar::number());
}
$> cargo run -- run scripts/book/items_imports/inline_modules.rn
3
And this is the equivalent modules loaded from the filesystem. These are three separate files:
mod foo;
mod bar;
dbg!(foo::number() + bar::number());
// file: ./foo/mod.rn
pub fn number() {
2
}
// file: ./bar.rn
pub fn number() {
1
}
$> cargo run -- run scripts/book/items_imports/modules.rn
3
Disambiguating imports
Normally an item would simply be used through its local name, such as
foo::number
above. But what happens if we need to reference a module which is
not a direct descendent of the current one or there is some ambiguation?
To this end Rune supports the following Rust keywords:
self
- which will resolve items from the root of the current module.crate
- which will look up items from the entrypoint of the current project.super
- which will look up items from the parent of the current module.
mod first {
pub fn number() {
crate::number() + 2
}
}
mod second {
pub fn number() {
super::first::number() + 4
}
}
pub fn number() {
1
}
dbg!(self::second::number());
$> cargo run -- run scripts/book/items_imports/item_keywords.rn
7
Visibility
Every item used has to be visible to that item. This is governed by Runes visibility rules, which are the following:
- An item can have inherited (empty) or a specified visibility like
pub
orpub(crate)
. - For an item to be visible, all of its parent items have to be visible.
- Items with inherited visibility are equivalent to
pub(self)
, making the item only visible in the module in which they are defined.
The available visibility modifiers are:
pub
- the item is visible from anywhere.pub(crate)
- the item is visible in the same crate.pub(super)
- the item is visible in the parent item only.pub(self)
- the item is only visible to other items in the same module.pub(in path)
- the item is only visible in the specified path. This is not supported yet.
Note that Rune doesn't have support for crates yet, meaning
pub(crate)
andpub
are currently effectively equivalent.