Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling Rust from Swift

Tags:

c

swift

rust

ffi

On the Rust side I wrote a function that returns a String as a pointer of bytes (laid out in memory as a C struct):

#[repr(C)]
pub struct RustByteSlice {
    pub bytes: *const u8,
    pub len: size_t,
}

#[no_mangle]
pub extern "C" fn get_string_from_rust() -> RustByteSlice {
    let s = "This is a string from Rust.";
    RustByteSlice {
        bytes: s.as_ptr(),
        len: s.len() as size_t,
    }
}

When generating a header file for it using cbindgen it gives me the following output:

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct {
  const uint8_t *bytes;
  size_t len;
} RustByteSlice;

RustByteSlice get_string_from_rust(void);

char *hello(const char *to);

void hello_release(char *s);

void utf8_bytes_to_rust(const uint8_t *bytes, size_t len);

In my Xcode project, this header is used as the bridging header, and the shared library compiled from the rust code is added to the dependency list. The header and include folders are defined in the build properties.

On the swift side I'm calling the rust function the following way:

struct RustByteSlice {
    var bytes: UnsafePointer<UInt8>
    var len: Int

    func asUnsafeBufferPointer() -> UnsafeBufferPointer<UInt8> {
        return UnsafeBufferPointer(start: bytes, count: len)
    }
    func asString(encoding: String.Encoding = String.Encoding.utf8) -> String? {
        return String(bytes: asUnsafeBufferPointer(), encoding: encoding)
    }
}

func strPtrRet() {
    let rustString: RustByteSlice = get_string_from_rust()

    if let stringFromRust = rustString.asString() {
        print("got a string from Rust: (stringFromRust)")
    } else {
        print("Could not parse Rust string as UTF-8")
    }
}

On the line let rustString: RustByteSlice = get_string_from_rust(), I get the following error:

Cannot convert value of type '__ObjC.RustByteSlice' to specified type 'ed25_ios_app.RustByteSlice'

How can I solve or work around this error ?

like image 957
Nicolas Marshall Avatar asked Mar 20 '20 22:03

Nicolas Marshall


1 Answers

After working through your code, you are redefining RustByteSlice.

From Using Imported C Structs and Unions in Swift, you don't need to redefine it as it automatically imports the struct.

The below swift code works.

func strPtrRet() -> String? {
    let rustString: RustByteSlice = get_string_from_rust()
    let buffer = UnsafeBufferPointer(start: rustString.bytes, count: rustString.len)
    let string = String(bytes: buffer, encoding: String.Encoding.utf8)

    if let stringFromRust = string {
        print("got a string from Rust: (stringFromRust)")
    } else {
        print("Could not parse Rust string as UTF-8")
    }

    return string
}

like image 194
NebulaFox Avatar answered Nov 19 '22 02:11

NebulaFox