Attribute Macro rune::function

source ·
#[function]
Expand description

Macro used to annotate native functions which can be loaded into rune.

This macro automatically performs the following things:

  • Rust documentation comments are captured so that it can be used in generated Rune documentation.
  • The name of arguments is captured to improve documentation generation.
  • If an instance function is annotated this is detected (if the function receives self). This behavior can be forced using #[rune::function(instance)] if the function doesn’t take self.
  • The name of the function can be set using the #[rune::function(path = name)] argument.
  • An associated function can be specified with the #[rune::function(path = Type::name)] argument. If instance is specified it is an associated instance function that can be defined externally.
  • Instance functions can be made a protocol function #[rune::function(protocol = STRING_DISPLAY)].

§Instance and associated functions

Instance and associated functions are a bit tricky to declare using #[rune::function], and care must be taken that you understand what needs to be done. So this section is dedicated to documenting the ins and outs of the process.

Briefly we should mention that instance functions are functions which are associated with a type at runtime. Calling a value like value.hello() invokes the hello associated function through the instance of value. The exact type of value will then be used to look up which function to call. They must take some kind of self parameter. Meanwhile associated functions are just functions which are associated with a static type. Like String::new(). The type String must then be in scope, and the function does not take a self parameter.

This is how you declare an instance function which takes &self or &mut self:

#[derive(Any)]
struct Struct {
    /* .. */
}

impl Struct {
    /// Get the length of the `Struct`.
    #[rune::function]
    fn len(&self) -> usize {
        /* .. */
    }
}

If a function does not take &self or &mut self, you must specify that it’s an instance function using #[rune::function(instance)]. The first argument is then considered the instance the function gets associated with:

#[derive(Any)]
struct Struct {
    /* .. */
}

/// Get the length of the `Struct`.
#[rune::function(instance)]
fn len(this: &Struct) -> usize {
    /* .. */
}

To declare an associated function which does not receive the type we must specify the path to the function using #[rune::function(path = Self::<name>)]:

#[derive(Any)]
struct Struct {
    /* .. */
}

impl Struct {
    /// Construct a new [`Struct`].
    #[rune::function(path = Self::new)]
    fn new() -> Struct {
        Struct {
           /* .. */
        }
    }
}

Or externally like this:

#[derive(Any)]
struct Struct {
    /* .. */
}

/// Construct a new [`Struct`].
#[rune::function(free, path = Struct::new)]
fn new() -> Struct {
    Struct {
       /* .. */
    }
}

The first part Struct in Struct::new is used to determine the type the function is associated with.

Protocol functions can either be defined in an impl block or externally. To define a protocol externally, you can simply do this:

#[derive(Any)]
struct Struct {
    /* .. */
}

#[rune::function(instance, protocol = STRING_DISPLAY)]
fn string_display(this: &Struct, f: &mut Formatter) -> VmResult<()> {
    /* .. */
}

§Examples

Defining and using a simple free function:

use rune::{Module, ContextError};

/// This is a pretty neat function which is called `std::str::to_uppercase("hello")`.
#[rune::function]
fn to_uppercase(string: &str) -> String {
    string.to_uppercase()
}

fn module() -> Result<Module, ContextError> {
    let mut m = Module::new();
    m.function_meta(to_uppercase)?;
    Ok(m)
}

A free instance function:

use rune::{Module, ContextError};

/// This is a pretty neat function, which is called like `"hello".to_uppercase()`.
#[rune::function(instance)]
fn to_uppercase(string: &str) -> String {
    string.to_uppercase()
}

/// This is a pretty neat function, which is called like `string::to_uppercase2("hello")`.
#[rune::function(path = string)]
fn to_uppercase2(string: &str) -> String {
    string.to_uppercase()
}

fn module() -> Result<Module, ContextError> {
    let mut m = Module::new();
    m.function_meta(to_uppercase)?;
    m.function_meta(to_uppercase2)?;
    Ok(m)
}

Regular instance and protocol functions:

use rune::{Any, Module, ContextError};
use rune::vm_write;
use rune::runtime::{Formatter, VmResult};
use rune::alloc::fmt::TryWrite;

#[derive(Any)]
struct String {
    inner: std::string::String
}

impl String {
    /// Construct a new string wrapper.
    #[rune::function(path = Self::new)]
    fn new(string: &str) -> Self {
        Self {
            inner: string.into()
        }
    }

    /// Uppercase the string inside of the string wrapper.
    ///
    /// # Examples
    ///
    /// ```rune
    /// let string = String::new("hello");
    /// assert_eq!(string.to_uppercase(), "HELLO");
    /// ```
    #[rune::function]
    fn to_uppercase(&self) -> String {
        String {
            inner: self.inner.to_uppercase()
        }
    }

    /// Display the string using the [`STRING_DISPLAY`] protocol.
    ///
    /// # Examples
    ///
    /// ```rune
    /// let string = String::new("hello");
    /// assert_eq!(format!("{}", string), "hello");
    /// ```
    #[rune::function(protocol = STRING_DISPLAY)]
    fn display(&self, f: &mut Formatter) -> VmResult<()> {
        vm_write!(f, "{}", self.inner);
        VmResult::Ok(())
    }
}

/// Construct a new empty string.
///
/// # Examples
///
/// ```rune
/// let string = String::empty();
/// assert_eq!(string, "hello");
/// ```
#[rune::function(free, path = String::empty)]
fn empty() -> String {
    String {
        inner: std::string::String::new()
    }
}

/// Lowercase the string inside of the string wrapper.
///
/// # Examples
///
/// ```rune
/// let string = String::new("Hello");
/// assert_eq!(string.to_lowercase(), "hello");
/// ```
#[rune::function(instance)]
fn to_lowercase(this: &String) -> String {
    String {
        inner: this.inner.to_lowercase()
    }
}

fn module() -> Result<Module, ContextError> {
    let mut m = Module::new();
    m.ty::<String>()?;
    m.function_meta(String::new)?;
    m.function_meta(empty)?;
    m.function_meta(String::to_uppercase)?;
    m.function_meta(to_lowercase)?;
    m.function_meta(String::display)?;
    Ok(m)
}

§Using vm_result and <expr>.vm?.

In order to conveniently deal with virtual machine errors which require use VmResult this attribute macro supports the vm_result option.

This changes the return value of the function to be VmResult, and ensures that any try operator use is wrapped as appropriate. The special operator <expr>.vm? is also supported in this context, which is a shorthand for the vm_try! macro.

use rune::alloc::String;
use rune::alloc::prelude::*;

#[rune::function(vm_result)]
fn trim(string: &str) -> String {
    string.trim().try_to_owned().vm?
}

This can be combined with regular uses of the try operator ?:

use core::str::Utf8Error;

use rune::alloc::String;
use rune::alloc::prelude::*;

#[rune::function(vm_result)]
fn trim_bytes(bytes: &[u8]) -> Result<String, Utf8Error> {
    Ok(core::str::from_utf8(bytes)?.trim().try_to_owned().vm?)
}