Rust is a statically typed language. If you want to compile and load part of the code separately, you need to process it through link.
First package a module into a dynamic library, and then call it when it runs.
In Windows, it is *.dll , Linux is *.so , and macos is *.dylib .
There are other more niche operating systems, which may have different suffixes...
The corresponding system on my side is macos.

First of all, for the separately compiled part, the Rust documentation gives more types, see the documentation,
https://doc.rust-lang.org/reference/linkage.html
My scenario uses the scheme dylib or cdylib Cargo.toml just specify the configuration.
I tried it, and both are cdylib for me. 06139a4c6dc62a is the support for C, I don't need it for the time being.

Then I loaded the dynamic library part and I used this library directly.
https://docs.rs/libloading/0.7.0/libloading/
But it should be noted that the loading process is more troublesome to pass parameters, and the complex structure will prompt "ffi unsafe".
The specific reason is not understood, it is probably related to the memory layout. I think sometimes it is a pointer when passing.
Directly usize or bool can be passed directly, but String and &str cannot be passed,
The solution given on the Internet is to convert between CString and CStr. There are examples here.
https://doc.rust-lang.org/std/ffi/struct.CStr.html

In addition, there are the details of externs writing. I copied the examples, then fixed the bugs, and finally got:

#[no_mangle]
pub unsafe extern "C" fn read_file(name_a: *const c_char) -> *mut c_char {
  let name = CStr::from_ptr(name_a).to_str().unwrap();
  let task = fs::read_to_string(&name);
  match task {
    Ok(s) => {
      let a = CString::new(s).unwrap();
      CString::into_raw(a)
    }
    Err(e) => {
      panic!("Failed to read file {:?}: {}", name, e)
    }
  }
}

Then the corresponding call part is:

fn main() {
  let u = call_dynamic();
  println!("Hello, world! {:?}", u);
}

fn call_dynamic() -> Result<String, Box<dyn std::error::Error>> {
  unsafe {
    let lib = libloading::Library::new(
      "/Users/chen/repo/calcit-lang/std/target/release/libcalcit_std.dylib",
    )?;
    let func: libloading::Symbol<unsafe extern "C" fn(name_a: *const c_char) -> *mut c_char> =
      lib.get(b"read_file")?;

    let a = CString::new("/Users/chen/repo/gist/rs-demo/Cargo.toml").expect("should not fail");
    let c_name = a.as_ptr(); // <-- 标记行A

    let ret = CStr::from_ptr(func(c_name)).to_str().unwrap();
    Ok(ret.to_owned())
  }
}

I encountered some pits in the middle, which caused the result to pass parameters for a long time and only get an empty string.
There are roughly two problems, one is the position of "marking line A", which needs to be defined as a variable to be normal.
At the beginning, I wrote it on the same line, and I could not get the content during debugging. Finally, I adjusted it by referring to the examples on the Internet.
Look at an article that mentions the reason that this is to give a a separate citation. (source is missing)

The other is the data returned from the dynamic library, *const char I originally used,
It is also a long time to read empty data. Finally, referring to an example of FFI, it was changed to *mut char then succeeded.
https://stackoverflow.com/a/42498913/883571
Still not sure what is going on, vaguely turned to a comment saying that in order to keep the data on the heap,
If it is the former, after the dynamic library function is called, the data will be crossed, and it will not be read. (The source is also lost)

In short, according to the above wording, after compilation, you can get the *.dylib file on macos and load it dynamically.

For the first time, there are still a lot of details that I didn't understand, including how to distribute it later, how to cross-platform, and I have no experience.
If you want to make progress in the future, you should leave notes early.


Update... I found that there is also a extern "Rust" , which corresponds to using Rust ABI directly.
Then you can directly use the various data types in Rust. It saves a lot of trouble.


题叶
17.3k 声望2.6k 粉丝

Calcit 语言作者