Go 调用Rust函数
在golang 中,go可以引用c代码,和c共舞,也就是cgo.在一些包里就能看到cgo的身影,gopcap(引用了libpcap),go-sqlite(引用了libsqlite).毕竟c是"万物之主"嘛.本文利用cgo实现go调rust函数
这是一个rust demo用于生成动态连接库for c
lib.rs 中的demo代码
#[no_mangle]
pub extern "C" fn say_hello(name:* const i8,age:std::ffi::c_int){let rname=unsafe{let realname=std::ffi::CStr::from_ptr(name);realname.to_str().expect("to string failed")};println!("hello {rname} your age is {age}");
}
cargo toml 添加此项生成动态库和静态库 for c.也可以二选一,没必要两个都编
[lib]
crate-type=["cdylib","staticlib"]
编译好后,只要引用了这个库就可以使用say_hello函数了.
go中引用say_hello代码
package main
//#cgo LDFLAGS: -L/Users/oswaldo/dev/rs/ffi/target/release -lffi
//extern void say_hello(const char*,int);
import "C"func main(){C.say_hello(C.CString("jim"),C.int(210))
}
运行看看效果
传个结构体交流交流
Go
package main//#cgo LDFLAGS: -L/Users/oswaldo/dev/rs/ffi/target/release -lffi
//#include <stdint.h>
//struct Student{uint8_t Age;uint8_t Score;uint16_t ID;};
//extern void say_hello(const char*,int);
//extern void get_student_info(struct Student*info);
import "C"type Student struct {Age uint8Score uint8ID uint16
}func main() {C.say_hello(C.CString("jim"), C.int(210))var student = C.struct_Student{Age: 22,Score: 90,ID: 0x0111,}C.get_student_info(&student)
}
Rust
#[repr(C)]是灵魂,不然以rust的方式对齐,和c是错位的。这个宏告诉编译器这个结构体以c的方式对齐。go结构体内存对齐方式和c一致(这也是为什么在go中无论是调某些syscall,还是我自己写的c库,我敢放心大胆的把go中结构体指针直接传过去,只要我内存构造和c中一致就不会出问题)
#[no_mangle]
pub extern "C" fn say_hello(name: *const i8, age: std::ffi::c_int) {let rname = unsafe {let realname = std::ffi::CStr::from_ptr(name);realname.to_str().expect("to string failed")};println!("hello {rname} your age is {age}");
}
/// repr(C)是灵魂,不然以rust的方式对齐,和c是错位的。这个宏告诉编译器这个结构体以c的方式对齐。go结构体内存对齐方式和c一致
#[repr(C)]
pub struct Student {pub age: u8,pub score: u8,pub id: u16,
}
#[no_mangle]
pub extern "C" fn get_student_info(student: *mut Student) {unsafe {let stu = student.as_ref().unwrap();let level=match stu.score {0 ..= 20 =>"C",21..=70=>"B",_=>"A"};println!("id: {},score: {}, age: {}, level {level}", stu.id, stu.score, stu.age);}
}