ocaml-interop 0.4.4

Utilities for Rust and OCaml interoperability
Documentation

ocaml-interop

build crate documentation license

Zinc-iron alloy coating is used in parts that need very good corrosion protection.

ocaml-interop is an OCaml<->Rust FFI with an emphasis on safety inspired by caml-oxide and ocaml-rs.

Read the full documentation here.

Report issues on Github.

Table of Contents

How does it work

ocaml-interop, just like caml-oxide, encodes the invariants of OCaml's garbage collector into the rules of Rust's borrow checker. Any violation of these invariants results in a compilation error produced by Rust's borrow checker.

This requires that the user is explicit about delimiting blocks that interact with the OCaml runtime, and that calls into the OCaml runtime are done only inside these blocks, and wrapped by a few special macros.

A quick taste

Convert between plain OCaml and Rust values

let rust_string = ocaml_string.to_rust();
let new_ocaml_string = to_ocaml!(gc, rust_string);

Convert between Rust and OCaml structs/records

(* OCaml *)
type my_record = {
  string_field: string;
  tuple_field: (string * int);
}
// Rust
struct MyStruct {
    string_field: String,
    tuple_field: (String, i64),
}

impl_conv_ocaml_record! {
    MyStruct {
        string_field: String,
        tuple_field: (String, i64),
    }
}

// ...

let rust_struct = ocaml_record.to_rust();
let new_ocaml_record = to_ocaml!(gc, rust_struct);

Convert between OCaml and Rust variants/enums

(* OCaml *)
type my_variant =
  | EmptyTag
  | TagWithInt of int
// Rust
enum MyEnum {
    EmptyTag,
    TagWithInt(i64),
}

impl_conv_ocaml_variant! {
    MyEnum {
        EmptyTag,
        TagWithInt(OCamlInt),
    }
}

// ...

let rust_enum = ocaml_variant.to_rust();
let new_ocaml_variant = to_ocaml!(gc, rust_enum);

Call OCaml functions from Rust

(* OCaml *)
Callback.register "ocaml_print_endline" print_endline
ocaml! {
    fn ocaml_print_endline(s: String);
}

// ...

let ocaml_string = to_ocaml!(gc, "hello OCaml!");
ocaml_call!(ocaml_print_endline(gc, ocaml_string)).unwrap();

Call Rust functions from OCaml

// Rust
ocaml_export! {
    pub fn twice_boxed_int(gc, num: OCaml<OCamlInt64>) -> OCaml<OCamlInt64> {
        let num = num.to_rust();
        let result = num * 2;
        ocaml_alloc!(result.to_ocaml(gc))
    }
}
(* OCaml *)
external rust_twice_boxed_int: int64 -> int64 = "twice_boxed_int"

(* ... *)

let result = rust_twice_boxed_int 123L in
(* ... *)

References and links