LOADING

加载过慢请开启缓存 浏览器默认开启

C和Rust混合探索

前言
先锋的同学鼓励对于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 通常为 i8u8
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 这个头文件就行了,可以调用了。好吧,这也是种省事的方法。