|
dune
|
We can interact with Rust in the same way we can interact with C: we use dune to build a static library of the rust code, and we later link this library to our ocaml code.
|
|
The magic happens within the `_build` directory, where dune will copy and build things out. Let's avoid copying the entire `target` repository that cargo creates.
|
(dirs :standard \ target)
|
Let's add a rule to build the rust static library every time we rune a dune command.
|
(rule
|
`target` specifies the file produced by the rule. You can also use `targets` if you need to list several files.
|
(target librust_thing.a)
|
dune will avoid re-running this rule every time, that is unless one of the dependencies listed in `deps` has changed.
|
(deps Cargo.toml lib.rs)
|
Here's the actual content of the rule, `progn` lists a series of commands that have to be executed in sequence. In our example, we run `cargo build` on our project, and we then extract the produced library.
|
(action
(progn
(run cargo build --release)
(run cp target/release/librust_thing.a .))))
|
Let's define the ocaml executable that will call the Rust code. Notice that it lists the library in the `foreign_archives` field.
|
(executable
(name main)
(foreign_archives rust_thing))
|
|
lib.rs
|
The derive macros `ocaml::IntoValue` and `ocaml::FromValue` allow you to convert entire types to OCaml types, which allows you to store them on the OCaml heap and have the OCaml's garbage collector handle them.
|
#[derive(ocaml::IntoValue, ocaml::FromValue, Debug)]
pub struct House {
name: String,
windows: u8,
door: bool,
cupboards: u64,
}
|
The `ocaml::func` derive macro can be used to export functions to OCaml. Here the returned value will be converted to an OCaml type thanks to the `ocaml::IntoValue` a few lines above.
|
#[ocaml::func]
pub fn build_house(name: String, windows: u8) -> House {
House {
name,
windows,
door: true,
cupboards: 4,
}
}
#[ocaml::func]
pub fn destroy_house(mut house: House) -> House {
house.door = false;
house
}
#[ocaml::func]
pub fn display_house(house: House) {
println!("{:?}", house);
}
|
The `ocaml::custom!` macro can be used to store custom types on the OCaml heap. As these will be ignored by the OCaml garbage collector, it is important to define a finalizer that will drop the value when it is garbage-collected. You have to use this when you can't implement ocaml::FromValue and ocaml::IntoValue for some type in your structure.
|
#[ocaml::func]
pub fn bump_windows(house: House, num: ocaml::Int) -> Option<House> {
if house.num + num >= 10 {
return None
} else {
Some(House {
windows: house.windows + num,
..house
})
}
}
#[derive(Debug, Clone)]
pub struct Opaque(String);
ocaml::custom!(Opaque {
finalize: finalizer,
});
unsafe extern "C" fn finalizer(v: ocaml::Raw) {
let ptr = v.as_pointer::<Opaque>();
ptr.drop_in_place()
}
unsafe impl ocaml::FromValue<'_> for Opaque {
fn from_value(value: ocaml::Value) -> Self {
let x: ocaml::Pointer<Self> = ocaml::FromValue::from_value(value);
x.as_ref().clone()
}
}
#[ocaml::func]
pub fn create_opaque() -> Opaque {
Opaque("sup".to_string())
}
#[ocaml::func]
pub fn print_opaque(opaque: Opaque) {
println!("{:?}", opaque);
}
|
you've reached the end!