前言:
先锋的同学鼓励对于C和Rust混合编程的探索,那就开始吧。
面试题内容之前我多多少少略有涉猎,但是Rust我还真没听过。
之前我一直认为C的恼人的手动管理内存和Java基本数据类型无法修改值等问题是存在于是就合理的,似乎是自它们诞生之初就无法改变的特点。但是目前看来Rust给了个解决方案。但是我觉得语言更不美了 :joy:
C和Rust混合探索:
Java、Python和C++ 之间的互调用常常依赖于各自的外部函数接口(FFI)或特定的绑定库。
但是ABI主要用于静态编译(源码转机器码,机器码转可执行文件)的语言,例如 C 和 C++。所以C和Rust使用ABI进行互相调用咯。
调用问题(上)
Rust调用C
在Rust中,先要链接c文件(夹)
#[link(name = "your_c")]
需要使用extern “C”来声明”我要调用c咯”
extern "C" {
fn add_numbers(a: i32, b: i32) -> i32;//问题:这是什么?
}
调用C语言代码时,Rust编译器无法保证内存安全,因为C语言没有内置的内存安全机制。因此,所有的调用都必须放在unsafe{ }里面。
fn main() {
unsafe {
let result = add_numbers(10, 20);
println!("sum: {}", result);
}
}
数据类型映射
好的,我们不得不先解决数据映射的问题。:worried:
众所周知,rust整数类型按位长度划分(如i8/i16/i32…u8/u16/u32…)且整数常量默认为i32,因此里面的a、b就是属于系统默认的整数类型,跟C里面的int很像对吧。
Rust的标准库提供了许多与C语言类型对应的类型,数据类型互通正如货币互通,这样两边才好说话吧。
直接叫ai帮忙造个表格~~~~懒得手打~~
C 语言类型 | Rust 对应类型 | 备注 |
---|---|---|
char |
c_char |
通常为 i8 或 u8 。 |
int |
c_int |
通常为 i32 。 |
float |
c_float |
相当于 f32 。 |
double |
c_double |
相当于 f64 。 |
void |
c_void |
用于指针,如 void * 对应 *mut c_void 。 |
char * |
*mut c_char |
指向 C 字符串的可变指针。 |
const char * |
*const c_char |
指向 C 字符串的不可变指针。 |
struct |
#[repr(C)] 的 struct |
必须使用 #[repr(C)] 宏保证内存布局兼容。 |
enum |
#[repr(C)] 的 enum |
必须使用 #[repr(C)] 宏保证大小和布局兼容。 |
调用问题(下)
好的好的,数据类型映射问题暂时解决了,继续。
C语言调用Rust函数时,首先需要将Rust函数暴露为C语言兼容的ABI。
具体方式是使用#[no_mangle]和extern “C”这两个属性来修饰函数。
#[no_mangle]//告诉编译器不要对函数名进行“名字重整”
pub extern "C" //按照C语言来编订这个函数
fn add_numbers(a: i32, b: i32) -> i32 {
a + b
}
第二步将Rust代码编译成一个C动态链接库或者静态链接库(这样C语言才能调用)如示例:
[lib]
name = "rust_lib"
crate-type = ["cdylib"]
然后运行
cargo build --release
生成的库文件将默认位于target/release/目录下。(先锋示例好像没有,是要我自己来吗?)
最后,在C语言代码中,就可以像调用其他C函数一样调用这个Rust函数。
extern int rust_add(int a, int b);// 记得声明Rust函数
但是,先锋示例也不是这样喵。
方法是提供了个头文件,有点像接口。
// rust_functions.h
#ifndef RUST_FUNCTIONS_H
#define RUST_FUNCTIONS_H
int add_numbers(int a, int b);
int multiply_numbers(int a, int b);
#endif
这样其他文件只需要 #include 这个头文件就行了,可以调用了。好吧,这也是种省事的方法。