ocaml 1.2.1

OCaml bindings for Rust
Documentation
[ocaml-rs](https://github.com/zshipko/ocaml-rs) is a Rust crate for interacting with the OCaml runtime. It allows you to write functions in Rust that can be called from OCaml and vice-versa. `ocaml-rs` also does automatic conversion between OCaml and Rust representations. There are several crates that make this possible:

* [ocaml-sys](https://crates.io/crates/ocaml-sys) - Low level bindings to the OCaml runtime
* [ocaml-boxroot-sys](https://crates.io/crates/ocaml-boxroot-sys) - Bindings to [ocaml-boxroot](https://gitlab.com/ocaml-rust/ocaml-boxroot/), which handles safe allocation of OCaml values
* [ocaml-derive](https://crates.io/crates/ocaml-derive) - Procedural macros: `ocaml::func`, `ocaml::sig`, `derive(FromValue)`, `derive(ToValue)`
* [ocaml-build](https://crates.io/crates/ocaml-build) - Generate OCaml interfaces from `ocaml::sig` definitions
* [ocaml](https://crates.io/crates/ocaml) - Higher level bindings built using the crates listed above

Before going any further, it may be helpful to read through the [Interfacing C with OCaml](https://v2.ocaml.org/manual/intfc.html) from the OCaml handbook if you haven't already!

## Initial setup

This section will cover how to set up a Rust crate that is linked into an OCaml program. If you're interested in calling into an OCaml library from Rust instead, see [Linking an OCaml library into a Rust program](./04_linking_an_ocaml_library_into_a_rust_program.md).

Add the following to your `Cargo.toml`:

```toml
[lib]
crate-type = ["staticlib"] # You can also use cdylib, depending on your project

[dependencies]
ocaml = "*"
```

Additionally, on macOS you may need to add a `.cargo/config` with the following:

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

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

If you plan on using `ocaml-build`:

```toml
[build-dependencies]
ocaml-build = "*"
```

And add a `build.rs`:

```rust,ignore
# extern crate ocaml_build;
pub fn main() -> std::io::Result<()> {
  ocaml_build::Sigs::new("src/rust.ml").generate()
}
```

This build script will look for usages of `#[ocaml::sig(...)]` to generate OCaml bindings.

Next you will need to add setup a [dune](https://dune.build) project to handle compilation of your OCaml code. Here is an example `dune` file that will link your Rust project, in this case the Rust crate is named `example`:

```ignore
(rule
 (targets libexample.a)
 (deps (glob_files *.rs))
 (action
  (progn
   (run cargo build --target-dir %{project_root}/../../target --release)
   (run mv %{project_root}/../../target/release/libexample.a libexample.a))))

(library
 (name example)
 (public_name example)
 (foreign_archives example)
 (c_library_flags
  (-lpthread -lc -lm)))
```

You should also add the following stanza to a `dune` file at the root of your project to ignore the `target` directory:

```ignore
(dirs :standard \ target)
```

It can take a little trial and error to get this right depending on the specifics of your project!

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`

To simplify the full setup process, take a look at [ocaml-rust-starter](https://github.com/zshipko/ocaml-rust-starter).

## 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.
- `OCAML_INTEROP_NO_CAML_STARTUP` (default: unset) can be set when loading an `ocaml-rs` library into an OCaml
  bytecode runtime (such as `utop`) to avoid linking issues with `caml_startup`

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 `ToValue`
- `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


## Writing your first `ocaml::func`

[ocaml::func](https://docs.rs/ocaml/latest/ocaml/attr.func.html) is the highest-level macro that can be used to generate OCaml functions. It's built on `ocaml::native_func` which only works on `Value` parameters and `ocaml::bytecode_func` which is used for generating bytecode functions. `ocaml::func` will take care of generating bytecode bindings for functions with more than five parameters as required by the OCaml runtime. `ocaml::func` handles using `CAMLparam`/`CAMLlocal`/`CAMLreturn` correctly for you, often making it much easier to write bindings than using the C API directly, particularly for those who haven't used the OCaml C API before.

All `ocaml::func`'s have an implicit `gc` variable which is used to access the OCaml runtime. To pick another name you can provide it as an argument to the `ocaml::func` macro:

```rust,ignore
#[ocaml::func(my_gc_name)]
...
```

The following example will read a file and return the contents, we will ignore error handling for now since that will be covered later - however, one thing worth mentioning is that Rust panics will be converted into OCaml exceptions.

```rust
# extern crate ocaml;

#[ocaml::func]   // This is needed to make the function compatible with OCaml
#[ocaml::sig("string -> string")] /// This is used to generate the OCaml bindings
pub unsafe fn read_file(filename: String) -> String {
  std::fs::read_to_string(filename).unwrap()
}
```

In the above example, automatic conversion is performed between OCaml strings and Rust strings. The next section will provide a table of valid conversions before getting into more details about writing functions and calling OCaml functions from Rust.