Functions
Together with struct
, functions are the core building blocks of hot reloading
in Mun. Throughout the documentation you've already seen a lot of examples of
the fn
keyword, which is used to define a function.
Mun uses snake case as the conventional style for function and variable names. In snake case all letters are lowercase and words are separated by underscores.
fn main() {
another_function();
}
fn another_function() {
}
Function definitions start with an optional access modifier (pub
), followed
by the fn
keyword, a name, an argument list enclosed by parentheses, an
optional return type specifier, and finally a body.
Marking a function with the pub
keyword allows you to publicly expose
that function, for usage in other modules or when hot reloading. Otherwise
the function will only be accessible from the current source file.
Function Access Modifier
Marking a function with the pub
keyword allows you to use it from outside of
the module it is defined in.
// This function is not accessible outside of this code
fn foo() {
// ...
}
// This function is accessible from anywhere.
pub fn bar() {
// Because `bar` and `foo` are in the same file, this call is valid.
foo()
}
When you want to interface from your host language (C++, Rust, etc.) with Mun,
you can only access pub
functions. These functions are hot reloaded by the
runtime when they or functions they call have been modified.
Function Arguments
Functions can have an argument list. Arguments are special variables that are part of the function signature. Unlike regular variables you have to explicitly specify the type of the arguments. This is a deliberate decision, as type annotations in function definitions usually mean that the compiler can derive types almost everywhere in your code. It also ensures that you as a developer define a contract of what your function can accept as its input.
The following is a rewritten version of another_function
that shows what an
argument looks like:
fn main() {
another_function(3);
}
fn another_function(x: i32) {
}
The declaration of another_function
specifies an argument x
of the i32
type. When you want a function to use multiple arguments, separate them with
commas:
fn main() {
another_function(3, 4);
}
fn another_function(x: i32, y: i32) {
}
Function Bodies
Function bodies are made up of a sequence of statements and expressions. Statements are instructions that perform some action and do not return any value. Expressions evaluate to a result value.
Creating a variable and assigning a value to it with the let
keyword is a
statement. In the following example, let y = 6;
is a statement.
fn main() {
let y = 6;
}
Statements do not return values and can therefore not be assigned to another variable.
Expressions do evaluate to something. Consider a simple math operation 5 + 6
,
which is an expression that evaluates to 11
. Expressions can be part of a
statement, as can be seen in the example above. The expression 6
is assigned
to the variable y
. Calling a function is also an expression.
The body of a function is just a block. In Mun, not just bodies, but all blocks
evaluate to the last expression in them. Blocks can therefore also be used on
the right hand side of a let
statement.
fn foo() -> i32 {
let bar = {
let b = 3;
b + 3
};
// `bar` has a value 6
bar + 3
}
Returning Values from Functions
Functions can return values to the code that calls them. We don't name return
values in the function declaration, but we do declare their type after an arrow
(->
). In Mun, a function implicitly returns the value of the last expression
in the function body. You can however return early from a function by using the
return
keyword and specifying a value.
fn five() -> i32 {
5
}
fn main() {
let x = five();
}
There are no function calls or statements in the body of the five
function,
just the expression 5
. This is perfectly valid Mun. Note that the return type
is specified too, as -> i32
.
Whereas the last expression in a block implicitly becomes that blocks return
value, explicit return
statements always return from the entire function:
fn foo() -> i32 {
let bar = {
let b = 3;
return b + 3;
};
// This code will never be executed
return bar + 3;
}