Module part1_initial_usage_a_brief_overview

Source
Expand description

§Part 1: Initial Usage - A Brief Overview

This section introduces basic examples to demonstrate the core functionality of [ocaml-interop].

§1.1 Exporting Rust Functions to OCaml: An Introduction

Rust functions can be exposed to OCaml utilizing the #[ocaml_interop::export] procedural macro.

Rust (src/lib.rs or designated Rust library):

use ocaml_interop::{OCaml, OCamlInt, OCamlRuntime, ToOCaml};

#[ocaml_interop::export]
fn rust_add_one(cr: &mut OCamlRuntime, num: OCaml<OCamlInt>) -> OCaml<OCamlInt> {
    let rust_num: i64 = num.to_rust();
    let result = rust_num + 1;
    result.to_ocaml(cr)
}

OCaml (e.g., main.ml):

(* Declare the external Rust function *)
external rust_add_one : int -> int = "rust_add_one"

let () =
  let five = rust_add_one 4 in
  Printf.printf "4 + 1 = %d\n" five (* Output: 4 + 1 = 5 *)

The #[ocaml_interop::export] macro manages FFI boilerplate and panic safety mechanisms. It exposes OCaml values to Rust in a type-safe manner; subsequent conversion to Rust types must be performed explicitly by the developer. These aspects will be detailed subsequently.

§1.2 Invoking OCaml Functions from Rust: An Introduction

To call OCaml functions from Rust, the ocaml! macro is typically employed subsequent to the registration of the OCaml function.

OCaml (e.g., my_ocaml_lib.ml):

let multiply_by_two x = x * 2

let () =
  Callback.register "multiply_by_two" multiply_by_two

This OCaml code must be compiled and linked with the Rust program.

Rust (main.rs):

use ocaml_interop::{
    OCaml, OCamlInt, OCamlRuntime, OCamlRuntimeStartupGuard, ToOCaml, BoxRoot
};

// Declare the OCaml function signature
mod ocaml_bindings {
    use ocaml_interop::{ocaml, OCamlInt};

    ocaml! {
        pub fn multiply_by_two(num: OCamlInt) -> OCamlInt;
    }
}

fn main() -> Result<(), String> {
    // Initialize the OCaml runtime if Rust is the primary execution context
    let _guard: OCamlRuntimeStartupGuard = OCamlRuntime::init()?;

    OCamlRuntime::with_domain_lock(|cr| {
        let rust_val: i64 = 10;
        // Convert to a rooted OCaml value.
        let ocaml_val: BoxRoot<OCamlInt> = rust_val.to_boxroot(cr);

        // Invoke the OCaml function
        // Arguments are passed as OCamlRef<T>; the return value is BoxRoot<T>
        let result_root: BoxRoot<OCamlInt> = ocaml_bindings::multiply_by_two(cr, &ocaml_val);

        let rust_result: i64 = result_root.to_rust(cr);
        println!("10 * 2 = {}", rust_result); // Output: 10 * 2 = 20
    });
    Ok(())
}

§1.3 The OCaml Runtime Handle: OCamlRuntime

Interactions with the OCaml runtime require access to an OCamlRuntime instance, conventionally named cr.

This handle is indispensable for managing OCaml’s state, memory, and domain locks.