#[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 takeself
. - 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. Ifinstance
is specified it is an associated instance function that can be defined externally. - Instance functions can be made a protocol function
#[rune::function(protocol = DISPLAY_FMT)]
.
§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 = DISPLAY_FMT)]
fn display_fmt(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 [`DISPLAY_FMT`] protocol.
///
/// # Examples
///
/// ```rune
/// let string = String::new("hello");
/// assert_eq!(format!("{}", string), "hello");
/// ```
#[rune::function(protocol = DISPLAY_FMT)]
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?)
}
§Using keep
to keep the name
By default, the name of the function is mangled and the metadata is given
the original name. This means you can’t easily call the function from both
Rune and Rust. This behaviour can be changed by using the keep
attribute, in
which case you must refer to the meta object by a mangled name
(specifically the function name with __meta
appended):
use rune::{Module, ContextError};
/// Don't mangle the name of the function
#[rune::function(keep)]
fn to_uppercase(string: &str) -> String {
string.to_uppercase()
}
fn module() -> Result<Module, ContextError> {
let mut m = Module::new();
m.function_meta(to_uppercase__meta)?;
Ok(m)
}
fn call_from_rust() {
assert_eq!(to_uppercase("hello"), "HELLO");
}