在Rust中调用Go动态库
2020/07/20
posted in
Rust
2020/07/20
posted in
Rust
最近在研究Rust和Go语言如何相互调用, 这样可以发挥两个语言各自的优势, 用Go语言能够进行快速的开发, 用Rust语言可以提升程序的性能和安全性
package main
import "C"
import "fmt"
//export Hello
func Hello(str string) {
fmt.Printf("Hello: %s!\n", str)
fmt.Printf("Hello: %#v!\n", str)
}
func main() {}
在编写Go动态库时需要
C
库main
函数//export 函数名
的形式指定需要导出的函数名我们使用go build
命令编译上面的源码
go build -buildmode=c-shared -o /Data/Rust/learn4libloading/src/plugins/libhello.so hello.go
我直接编译到Rust代码的目录中了
这样, 我们在编译的目录中会多出两个文件libhello.so
文件和libhello.h
文件
libhello.so
就是动态库了, 而libhello.h
是C
的头文件
我们可以看一下libhello.h
的内容
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h> /* for ptrdiff_t below */
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif
#endif
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern void Hello(GoString p0);
#ifdef __cplusplus
}
#endif
这其中对我们来说最重要的内容就是Go
中的类型在C
中是如何定义的, 只有知道这些信息, 我们才能在Rust
中调用相应的类型构建Go
动态库中需要用到的类型
在Rust
代码中我用到了一个crate
叫做libloading
extern crate libloading as lib;
use std::ffi::CString;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct GoString {
pub p: *const ::std::os::raw::c_char,
pub n: isize,
}
fn callGolang(go_name: GoString) -> Result<u32, Box<dyn std::error::Error>> {
let lib = lib::Library::new("/Data/Rust/learn4libloading/src/plugins/libhello.so")?;
unsafe {
let func: lib::Symbol<unsafe extern fn(GoString)> = lib.get(b"Hello")?;
func(go_name);
Ok(0)
}
}
fn main() {
let c_name = CString::new("Alex").unwrap();
let go_str_ref = GoString {
p: c_name.as_ptr(),
n: c_name.as_bytes().len() as isize,
};
let _ = callGolang(go_str_ref);
}
其中的关键是, 先定义了一个GoString
结构体, 对应Go
语言中的string
类型.
然后用libloading
加载动态库, 获取我们需要的函数, 再将对应的GoString
类型参数传递过去.