我应该如何使用&str,Option和String编组Rust函数并在C#中使用它?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我应该如何使用&str,Option和String编组Rust函数并在C#中使用它?相关的知识,希望对你有一定的参考价值。

以下代码在Rust中:

#[no_mangle]
#[cfg(not(target_arch = "wasm32"))]
pub extern fn generate_work(input_hash: &str, max_iters: Option<u64>) -> Option<String> {
    let bytes = Vec::from_hex(input_hash).unwrap();
    generate_work_internal(&bytes[..], max_iters)
}

我在C#中有以下代码:

[DllImport("mydll.dll")]
private static extern string generate_work(string input_hash, ulong[] max_iters);

我收到错误:

System.AccessViolationException:'尝试读取或写入受保护的内存。这通常表明其他记忆已经腐败。

我尝试过其他签名,但没有一个签名。

答案

如我的FFI Omnibus中所述,您无法通过FFI函数传递复杂的特定于Rust的类型。您必须仅使用C ABI已知的类型。在您的示例中,这些类型未知:

  • &str
  • Option
  • String

相反,您将只需要使用基本的C指针。 NULL指针可以表示字符串的None,但对于Option<u64>,您需要将其分解为两个值,布尔值和实际值。你的FFI功能看起来像:

extern crate libc;

use std::ffi::{CStr, CString};
use std::ptr;

#[no_mangle]
#[cfg(not(target_arch = "wasm32"))]
pub extern "C" fn generate_work(
    input_hash: *const libc::c_char,
    max_iters_present: bool,
    max_iters: u64,
) -> *const libc::c_char {
    let input_hash = if input_hash.is_null() {
        return ptr::null();
    } else {
        unsafe { CStr::from_ptr(input_hash) }
    };
    let input_hash = match input_hash.to_str() {
        Ok(s) => s,
        Err(_) => return ptr::null(),
    };
    let max_iters = if max_iters_present {
        Some(max_iters)
    } else {
        None
    };

    let result = inner_code(input_hash, max_iters);

    match result {
        Some(s) => {
            match CString::new(s) {
                Ok(s) => s.into_raw(),
                Err(_) => ptr::null(),
            }
        },
        None => ptr::null(),
    }
}

请注意,这将返回一个已分配的字符串,您需要将其传递回Rust以取消分配。再次,如the FFI Omnibus所述,你需要类似的东西

#[no_mangle]
pub extern fn free_a_string(s: *mut c_char) {
    unsafe {
        if s.is_null() { return }
        CString::from_raw(s)
    };
}

Converting a string to Rust is easy

[DllImport("string_arguments", EntryPoint="how_many_characters")]
public static extern uint HowManyCharacters(string s);

Returning a string requires a lot more trickery, sadly

internal class Native
{
    [DllImport("string_return")]
    internal static extern ThemeSongHandle theme_song_generate(byte length);
    [DllImport("string_return")]
    internal static extern void theme_song_free(IntPtr song);
}

internal class ThemeSongHandle : SafeHandle
{
    public ThemeSongHandle() : base(IntPtr.Zero, true) {}

    public override bool IsInvalid
    {
        get { return false; }
    }

    public string AsString()
    {
        int len = 0;
        while (Marshal.ReadByte(handle, len) != 0) { ++len; }
        byte[] buffer = new byte[len];
        Marshal.Copy(handle, buffer, 0, buffer.Length);
        return Encoding.UTF8.GetString(buffer);
    }

    protected override bool ReleaseHandle()
    {
        Native.theme_song_free(handle);
        return true;
    }
}

也可以看看:

以上是关于我应该如何使用&str,Option和String编组Rust函数并在C#中使用它?的主要内容,如果未能解决你的问题,请参考以下文章

使用类型中的“in”键入具有计算属性的类型

Scala spark 如何与 List[Option[Map[String, DataFrame]]] 交互

Java - String.lastIndexOf(str)逻辑我无法理解

用Scala中的Option包装Java中的空返回方法?

如何从这种String中获取关键字

我想优化使用 IN 子句和 regex_str 函数的存储过程。我不确定我应该如何进一步优化它?