สร้าง Elixir Project ด้วย mix
$ mix new ferris_ex --module FerrisEx
ใส่ rustler
เข้าไปใน mix dependencies
defp deps do
[
{:rustler, "~> 0.21.1"}
]
end
หลังจากนั้นก็สั่ง mix deps.get
เพื่อ fetch dependencies
ต่อมา generate rust project ผ่าน rustler
ก็จะมี prompt ให้ใส่ชื่อ module ลงไป ผมใส่ module ชื่อ FerrisEx.Native
และชื่อ rust library ก็ตามน้ำมันไป
$ mix rustler.new
==> toml
Compiling 10 files (.ex)
Generated toml app
==> rustler
Compiling 5 files (.ex)
Generated rustler app
==> ferris_ex
This is the name of the Elixir module the NIF module will be registered to.
Module name > FerrisEx.Native
This is the name used for the generated Rust crate. The default is most likely fine.
Library name (ferrisex_native) >
* creating native/ferrisex_native/.cargo/config
* creating native/ferrisex_native/README.md
* creating native/ferrisex_native/Cargo.toml
* creating native/ferrisex_native/src/lib.rs
Ready to go! See /Users/thanabodee/src/github.com/wingyplus/ferris_ex/native/ferrisex_native/README.md for further instructions.
ต่อมาเปิด Cargo.toml ซึ่งอยู่ใน $PWD/native/ferrisex_native/Cargo.toml
แล้วทำการใส่ library ชื่อ ferris_says
ลงไป
[dependencies]
rustler = "0.21.1"
lazy_static = "1.0"
...
ferris-says = "0.2"
และทำการแก้โค้ดของ lib.rs ให้เป็น function ตามที่เราต้องการ
use ferris_says;
use rustler::{Encoder, Env, Error, Term};
use std::io;
use std::str;
mod atoms {
rustler::rustler_atoms! {
atom ok;
}
} // 1
rustler::rustler_export_nifs! {
"Elixir.FerrisEx.Native",
[
("say", 1, say)
],
None
} // 2
fn say<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error> {
let text: String = args[0].decode()?;
let mut cursor = io::Cursor::new(Vec::new());
ferris_says::say(&text.into_bytes(), 24, &mut cursor).unwrap();
Ok((atoms::ok(), str::from_utf8(&cursor.into_inner()).unwrap()).encode(env))
} // 3
จาก snippet ข้างบน
- ประกาศ atom ที่เราจะใช้ใน rust library
- ประกาศ function ใน module
FerrisEx.Native
- implement function
เสร็จแล้วก็สั่ง cargo build
เพื่อให้ rust สร้าง dynamic library ขึ้นมา
ต่อมาทำการสร้าง elixir module เพื่อ binding กับ rust library ของเรา
# ./lib/ferris_ex/native.ex
defmodule FerrisEx.Native do
use Rustler, otp_app: :ferris_ex, crate: :ferrisex_native
def say(_text), do: :erlang.nif_error(:nif_not_loaded)
end
เพื่อที่จะทำการ binding กับ rust library ของเราได้ต้องใช้ use Rustler
และบอกว่าจะผูกกับ crate ชื่ออะไรของเรา และต้องมี function dummy เพื่อกรณีที่ elixir binding nif ไม่ได้
เสร็จแล้วก็ copy dynamic library ที่ได้จาก cargo build
มาไว้ที่ priv/native
ของ elixir
$ cp ./native/ferrisex_native/target/debug/libferrisex_native.dylib ./priv/native/libferrisex_native.so
หลังจาก copy แล้วก็ทำการเปิด iex ขึ้นมาเพื่อทดสอบ binding ของเรา
iex(1)> {:ok, out} = FerrisEx.Native.say("Hello, I'm Ferris.")
{:ok,
" ____________________\n< Hello, I'm Ferris. >\n --------------------\n \\\n \\\n _~^~^~_\n \\) / o o \\ (/\n '_ - _'\n / '-----' \\\n"}
iex(2)> IO.puts(out)
____________________
< Hello, I'm Ferris. >
--------------------
\
\
_~^~^~_
\) / o o \ (/
'_ - _'
/ '-----' \
:ok
Top comments (0)