Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Foreign Functions

Functions implemented in Rust can be called from Abra. Foreign functions must be declared ahead of time in an Abra file, so that the compiler knows they exist. The compiler will search for a directory with the same name as the Abra file which contains a Rust project. That Rust project should use crate-type "cdylib" so that it can be loaded at runtime. It must take abra_core as a dependency. It is recommended to take abra_core as a build-dependency as well and to invoke generate_bindings_for_crate() in the project's build.rs. This will automatically generate the necessary unsafe glue code that corresponds with the foreign function declarations. Then, you can implement those foreign functions using safe Rust.

In order to avoid creating a Rust project (and therefore a cdylib) for every Abra file with a foreign function declaration, there should be a single Rust project for every toplevel namespace that contains foreign function declarations. For example, implementing a foreign function in os.abra (os is under the root namespace) should be done in os/rust_project/src/os.rs. Implementing a foreign function in os/exec.abra, which is under the os/ namespace, should be done in os/rust_project/src/os/exec.abra -- the same Rust project.

os.abra

foreign fn fread(path: string) -> string

foreign fn fwrite(path: string, contents: string) -> string

os/rust_project/Cargo.toml

[package]
name = "abra_module_os"

[lib]
crate-type = ["cdylib"]
[dependencies]
abra_core = { workspace = true }

[build-dependencies]
abra_core = { workspace = true }

os/rust_project/build.rs

fn main() {
    abra_core::addons::generate_bindings_for_crate();
}

os/rust_project/src/lib.rs (automatically generated)

use abra_core::vm::Vm;
use std::fs;

#[no_mangle]
pub unsafe extern "C" fn fread(vm: *mut Vm) {
    let string_view = vm_view_string(vm);
    let path = string_view.to_owned();
    vm_pop(vm);

    let content = fs::read_to_string(path).expect("Unable to read from file");

    let string_view = StringView::from_string(&content);
    vm_push_string(vm, string_view);
}

#[no_mangle]
pub unsafe extern "C" fn fwrite(vm: *mut Vm) {
    let string_view = vm_view_string(vm);
    let content = string_view.to_owned();
    vm_pop(vm);

    // TODO: make a macro for this called get_string!
    let string_view = vm_view_string(vm);
    let path = string_view.to_owned();
    vm_pop(vm);

    fs::write(path, content).expect("Unable to write to file");
}

os/rust_project/src/os.rs

use std::fs::self;
use std::io::Write;

pub fn fread(path: String) -> Result<String, String> {
    fs::read_to_string(path).map_err(|e| e.to_string())
}

pub fn fwrite(path: String, content: String) {
    fs::write(path, content).expect("Unable to write to file");
}