ocaml 0.21.0

OCaml bindings for Rust
Documentation
# ocaml-rs - OCaml extensions in Rust

<a href="https://crates.io/crates/ocaml">
    <img src="https://img.shields.io/crates/v/ocaml.svg">
</a>

`ocaml-rs` allows for OCaml extensions to be written directly in Rust with no C stubs. It was originally forked from [raml](https://crates.io/crates/raml), but has been almost entirely re-written thanks to support from the [OCaml Software Foundation](http://ocaml-sf.org/).

Works with OCaml versions `4.06.0` and up

Please report any issues on [github](https://github.com/zshipko/ocaml-rs/issues)

NOTE: While `ocaml-rs` *can* be used safely, it does not prevent a wide range of potential errors or mistakes. It should be thought of as a Rust implementation of the existing C API. [ocaml-interop](https://github.com/simplestaking/ocaml-interop) can be used to perform safe OCaml/Rust interop. The latest version of `ocaml-rs` actually uses `ocaml-interop` behind the scenes to interact with the garbage collector. `ocaml-rs` also exports an `interop` module, which is an alias for `ocaml_interop` and the two interfaces can be combined if desired.

### Getting started

Take a look at the [ocaml-rust-starter](http://github.com/zshipko/ocaml-rust-starter) project for a basic example to help get started with `ocaml-rs`.

On the Rust side, you will need to add the following to your `Cargo.toml`:

```toml
ocaml = "*"
```

or

```toml
ocaml = {git = "https://github.com/zshipko/ocaml-rs"}
```

For macOS you will need also to add the following to your project's `.cargo/config` file:

```toml
[build]
rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup"]
```

This is because macOS doesn't allow undefined symbols in dynamic libraries by default.

Additionally, if you plan on releasing to [opam](https://github.com/ocaml/opam), you will need to vendor your Rust dependencies to avoid making network requests during the build phase, since reaching out to crates.io/github will be blocked by the opam sandbox. To do this you should run:

```shell
cargo vendor
```

then follow the instructions for editing `.cargo/config`

### Build options

By default, building `ocaml-sys` will invoke the `ocamlopt` command to figure out the version and location of the OCaml compiler. There are a few environment variables to control this.

- `OCAMLOPT` (default: `ocamlopt`) is the command that will invoke `ocamlopt`
- `OCAML_VERSION` (default: result of `$OCAMLOPT -version`) is the target runtime OCaml version.
- `OCAML_WHERE_PATH` (default: result of `$OCAMLOPT -where`) is the path of the OCaml standard library.

If both `OCAML_VERSION` and `OCAML_WHERE_PATH` are present, their values are used without invoking `ocamlopt`. If any of those two env variables is undefined, then `ocamlopt` will be invoked to obtain both values.

Defining the `OCAML_VERSION` and `OCAML_WHERE_PATH` variables is useful for saving time in CI environments where an OCaml install is not really required (to run `clippy` for example).

### Features

- `derive`
  * enabled by default, adds `#[ocaml::func]` and friends and `derive` implementations for `FromValue` and `IntoValue`
- `link`
  * link the native OCaml runtime, this should only be used when no OCaml code will be linked statically
- `no-std`
  * Allows `ocaml` to be used in `#![no_std]` environments like MirageOS

### Documentation

[https://docs.rs/ocaml](https://docs.rs/ocaml)

### Examples

```rust
// Automatically derive `IntoValue` and `FromValue`
#[derive(ocaml::IntoValue, ocaml::FromValue)]
struct Example<'a> {
    name: &'a str,
    i: ocaml::Int,
}


#[ocaml::func]
pub fn incr_example(mut e: Example) -> Example {
    e.i += 1;
    e
}

#[ocaml::func]
pub fn build_tuple(i: ocaml::Int) -> (ocaml::Int, ocaml::Int, ocaml::Int) {
    (i + 1, i + 2, i + 3)
}

#[ocaml::func]
pub fn average(arr: ocaml::Array<f64>) -> Result<f64, ocaml::Error> {
    let mut sum = 0f64;

    for i in 0..arr.len() {
        sum += arr.get_double(i)?;
    }

    Ok(sum / arr.len() as f64)
}

// A `native_func` must take `ocaml::Value` for every argument and return an `ocaml::Value`
// these functions have minimal overhead compared to wrapping with `func`
#[ocaml::native_func]
pub fn incr(value: ocaml::Value) -> ocaml::Value {
    let i = value.int_val();
    ocaml::Value::int(i + 1)
}

// This is equivalent to:
#[no_mangle]
pub extern "C" fn incr2(value: ocaml::Value) -> ocaml::Value {
    ocaml::body!(gc: (value) {
        let i = value.int_val();
        ocaml::Value::int( i + 1)
    })
}

// `ocaml::native_func` is responsible for:
// - Ensures that #[no_mangle] and extern "C" are added, in addition to wrapping
// - Wraps the function body using `ocaml::body!`

// Finally, if your function is marked [@@unboxed] and [@@noalloc] in OCaml then you can avoid
// boxing altogether for f64 arguments using a plain C function and a bytecode function
// definition:
#[no_mangle]
pub extern "C" fn incrf(input: f64) -> f64 {
    input + 1.0
}

#[cfg(feature = "derive")]
#[ocaml::bytecode_func]
pub fn incrf_bytecode(input: f64) -> f64 {
    incrf(input)
}
```

Note: By default the `func` macro will create a bytecode wrapper (using `bytecode_func`) for functions with more than 5 arguments.

The OCaml stubs would look like this:

```ocaml
type example = {
    name: string;
    i: int;
}

external incr_example: example -> example = "incr_example"
external build_tuple: int -> int * int * int = "build_tuple"
external average: float array -> float = "average"
external incr: int -> int = "incr"
external incr2: int -> int = "incr2"
external incrf: float -> float = "incrf_bytecode" "incrf" [@@unboxed] [@@noalloc]
```

For more examples see [test/src](https://github.com/zshipko/ocaml-rs/blob/master/test/src) or [ocaml-vec](https://github.com/zshipko/ocaml-vec).

### Type conversion

This chart contains the mapping between Rust and OCaml types used by `ocaml::func`

| Rust type        | OCaml type           |
| ---------------- | -------------------- |
| `()`             | `unit`               |
| `isize`          | `int`                |
| `usize`          | `int`                |
| `i8`             | `int`                |
| `u8`             | `int`                |
| `i16`            | `int`                |
| `u16`            | `int`                |
| `i32`            | `int32`              |
| `u32`            | `int32`              |
| `i64`            | `int64`              |
| `u64`            | `int64`              |
| `f32`            | `float`              |
| `f64`            | `float`              |
| `str`            | `string`             |
| `[u8]`           | `bytes`              |
| `String`         | `string`             |
| `Option<A>`      | `'a option`          |
| `Result<A, B>`   | `exception`          |
| `(A, B, C)`      | `'a * 'b * 'c`       |
| `&[Value]`       | `'a array` (no copy) |
| `Vec<A>`, `&[A]` | `'a array`           |
| `BTreeMap<A, B>` | `('a, 'b) list`      |
| `LinkedList<A>`  | `'a list`            |

NOTE: Even though `&[Value]` is specifically marked as no copy, any type like `Option<Value>` would also qualify since the inner value is not converted to a Rust type. However, `Option<String>` will do full unmarshaling into Rust types. Another thing to note: `FromValue` for `str` and `&[u8]` is zero-copy, however `IntoValue` for `str` and `&[u8]` creates a new value - this is necessary to ensure the string is registered with the OCaml runtime.

If you're concerned with minimizing allocations/conversions you should use `Value` type directly.

#### Pointers to Rust values on the OCaml heap

`Pointer<T>` can be used to create and access Rust types on the OCaml heap.

For example, for a type that implements `Custom`:

```rust
use ocaml::FromValue;

struct MyType;

unsafe extern "C" fn mytype_finalizer(v: ocaml::Value) {
    let ptr: ocaml::Pointer<MyType> = ocaml::Pointer::from_value(v);
    ptr.drop_in_place()
}

ocaml::custom_finalize!(MyType, mytype_finalizer);

#[ocaml::func]
pub fn new_my_type() -> ocaml::Pointer<MyType> {
    ocaml::Pointer::alloc_custom(gc, MyType)
    // ocaml::Pointer::alloc_final(gc, MyType, finalizer, None) can also be used
    // if you don't intend to implement `Custom`
}

#[ocaml::func]
pub fn my_type_example(t: ocaml::Pointer<MyType>) {
    let my_type = t.as_mut();
    // MyType has no fields, but normally you
    // would do something with MyType here
}
```

#### Custom exception type

When a Rust `panic` or `Err` is encountered it will be raised as a `Failure` on the OCaml side, to configure a custom exception type you can register it with the OCaml runtime using the name `Rust_exception`:

```ocaml
exception Rust

let () = Callback.register_exception "Rust_error" (Rust "")
```

It must take a single `string` argument.

## Upgrading

Since 0.10 and later have a much different API compared to earlier version, here is are some major differences that should be considered when upgrading:

- `FromValue` and `IntoValue` have been marked `unsafe` because converting OCaml values to Rust and back also depends on the OCaml type signature.
  * A possible solution to this would be a `cbindgen` like tool that generates the correct OCaml types from the Rust code
- `IntoValue` now takes ownership of the value being converted
- The `caml!` macro has been rewritten as a procedural macro called `ocaml::func`, which performs automatic type conversion
  * `ocaml::native_func` and `ocaml::bytecode_func` were also added to create functions at a slightly lower level
  * `derive` feature required
- Added `derive` implementations for `IntoValue` and `FromValue` for stucts and enums
  * `derive` feature required
- `i32` and `u32` now map to OCaml's `int32` type rather than the `int` type
  * Use `ocaml::Int`/`ocaml::Uint` to refer to the OCaml's `int` types now
- `Array` and `List` now take generic types
- Strings are converted to `str` or `String`, rather than using the `Str` type
- Tuples are converted to Rust tuples (up to 20 items), rather than using the `Tuple` type
- The `core` module has been renamed to `sys` and is now just an alias for the `ocaml-sys` crate and all sub-module have been removed