Field functions
Field functions are special operations which operate on fields. These are distinct from associated functions, because they are invoked by using the operation associated with the kind of the field function.
The most common forms of fields functions are getters and setters, which are
defined through the Protocol::GET
and Protocol::SET
protocols.
The Any
derive can also generate default implementations of these through
various #[rune(...)]
attributes:
#[derive(Any)]
struct External {
#[rune(get, set, add_assign, copy)]
number: i64,
#[rune(get, set)]
string: String,
}
Once registered, this allows External
to be used like this in Rune:
pub fn main(external) {
external.number = external.number + 1;
external.number += 1;
external.string = `${external.string} World`;
}
The full list of available field functions and their corresponding attributes are:
Protocol | Attribute | |
---|---|---|
Protocol::GET | #[rune(get)] | For getters, like external.field . |
Protocol::SET | #[rune(set)] | For setters, like external.field = 42 . |
Protocol::ADD_ASSIGN | #[rune(add_assign)] | The += operation. |
Protocol::SUB_ASSIGN | #[rune(sub_assign)] | The -= operation. |
Protocol::MUL_ASSIGN | #[rune(mul_assign)] | The *= operation. |
Protocol::DIV_ASSIGN | #[rune(div_assign)] | The /= operation. |
Protocol::BIT_AND_ASSIGN | #[rune(bit_and_assign)] | The &= operation. |
Protocol::BIT_OR_ASSIGN | #[rune(bit_or_assign)] | The bitwise or operation. |
Protocol::BIT_XOR_ASSIGN | #[rune(bit_xor_assign)] | The ^= operation. |
Protocol::SHL_ASSIGN | #[rune(shl_assign)] | The <<= operation. |
Protocol::SHR_ASSIGN | #[rune(shr_assign)] | The >>= operation. |
Protocol::REM_ASSIGN | #[rune(rem_assign)] | The %= operation. |
The manual way to register these functions is to use the new Module::field_function
function. This clearly showcases that there's no relationship between the field
used and the function registered:
use rune::{Any, Module};
use rune::runtime::Protocol;
#[derive(Any)]
struct External {
}
impl External {
fn field_get(&self) -> String {
String::from("Hello World")
}
}
let mut module = Module::new();
module.field_function(&Protocol::GET, "field", External::field_get)?;
Would allow for this in Rune:
pub fn main(external) {
println!("{}", external.field);
}
Custom field function
Using the Any
derive, you can specify a custom field function by using an
argument to the corresponding attribute pointing to the function to use instead.
The following uses an implementation of add_assign
which performs checked
addition:
use rune::runtime::{VmError, VmResult};
use rune::termcolor::{ColorChoice, StandardStream};
use rune::{Any, ContextError, Diagnostics, Module, Vm};
use std::sync::Arc;
#[derive(Any)]
struct External {
#[rune(add_assign = External::value_add_assign)]
value: i64,
}
#[allow(clippy::unnecessary_lazy_evaluations)]
impl External {
fn value_add_assign(&mut self, other: i64) -> VmResult<()> {
self.value = rune::vm_try!(self.value.checked_add(other).ok_or_else(VmError::overflow));
VmResult::Ok(())
}
}
fn main() -> rune::support::Result<()> {
let m = module()?;
let mut context = rune_modules::default_context()?;
context.install(m)?;
let runtime = Arc::new(context.runtime()?);
let mut sources = rune::sources! {
entry => {
pub fn main(e) {
e.value += 1;
}
}
};
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(runtime, Arc::new(unit));
let input = External { value: i64::MAX };
let err = vm.call(["main"], (input,)).unwrap_err();
println!("{:?}", err);
Ok(())
}
fn module() -> Result<Module, ContextError> {
let mut m = Module::new();
m.ty::<External>()?;
Ok(m)
}
$> cargo run --example checked_add_assign
Error: numerical overflow (at inst 2)