Functions

One of the most common things in all of programming are functions. These are stored procedures which take a arguments, do some work, and then return. Functions are used because they encapsulate what they do so that the programmer only needs to concern itself with the protocol of the function.

What does it do? What kind of arguments does it take? The alternative would be to copy the code around and that wouldn't be very modular. Functions instead provide a modular piece of code that can be called and re-used. Over and over again.

fn keyword

In Rune, functions are declared with the fn keyword. You've already seen one which is used in every example, main. This is not a special function, but is simply what the Rune cli looks for when deciding what to execute.

pub fn main() {
    println("Hello World");
}
$> cargo run --bin rune -- run scripts/book/functions/main_function.rn
Hello World

In Rune, you don't have to specify the return type of a function. Given that Rune is a dynamic programming language, this allows a function to return anything, even completely distinct types.

fn foo(condition) {
    if condition {
        "Hello"
    } else {
        1
    }
}

pub fn main() {
    println!("{}", foo(true));
    println!("{}", foo(false));
}
$> cargo run --bin rune -- run scripts/book/functions/return_value.rn
Hello
1

Depending on who you talk to, this is either the best thing since sliced bread or quite scary. It allows for a larger ability to express a program, but at the same time it can be harder to reason on what your program will do.

Calling functions in Rust

Rune functions can be easily set up and called from Rust.

use rune::termcolor::{ColorChoice, StandardStream};
use rune::{Diagnostics, Vm};

use std::sync::Arc;

fn main() -> rune::support::Result<()> {
    let context = rune_modules::default_context()?;

    let mut sources = rune::sources!(
        entry => {
            pub fn main(number) {
                number + 10
            }
        }
    );

    let mut diagnostics = Diagnostics::new();

    let result = rune::prepare(&mut sources)
        .with_context(&context)
        .with_diagnostics(&mut diagnostics)
        .build();

    if !diagnostics.is_empty() {
        let mut writer = StandardStream::stderr(ColorChoice::Always);
        diagnostics.emit(&mut writer, &sources)?;
    }

    let unit = result?;

    let mut vm = Vm::new(Arc::new(context.runtime()?), Arc::new(unit));
    let output = vm.execute(["main"], (33i64,))?.complete().into_result()?;
    let output: i64 = rune::from_value(output)?;

    println!("output: {}", output);
    Ok(())
}
$> cargo run --example minimal
output: 43