结构体
实践的步骤分为以下几步:
1.Rust 导出一个 so 库
2.main 函数在 C 这边,链接 Rust 的 so 库
3.C 中分配栈内存,交由 Rust 端填充
4.Rust 端打印
5.C 端打印
Rust端的代码:
// src/lib.rs
use std::os::raw::c_int;
use std::slice;
#[repr(C)]
#[derive(Debug)]
pub struct Student {
pub num: c_int,
pub total: c_int,
}
#[no_mangle]
pub extern "C" fn fill_students(p_stu: *mut Student, n: c_int) {
assert!(!p_stu.is_null());
let s: &mut [Student] = unsafe { slice::from_raw_parts_mut(p_stu, n as usize) };
for elem in s.iter_mut() {
// fill any valid values
elem.num = 1 as c_int;
elem.total = 100 as c_int;
}
}
#[no_mangle]
pub extern "C" fn print_students(p_stu: *mut Student, n: c_int) {
assert!(!p_stu.is_null());
let s: &[Student] = unsafe { slice::from_raw_parts(p_stu, n as usize) };
for elem in s.iter() {
println!("print in rust side: {:?}", elem);
}
}
代码解释:
1.Rust代码中定义了一个结构体,有两个函数,一个是对C端传来的结构体进行填充,另一个是打印C端传来的结构体。
2.C代码中是一个生成一个结构体数组,传入数组名,这边用结构体指针进行接收,传数组之前说过需要告知长度,s就是可变切片类型,另一个打印不需要可变类型,这里注意Rust对于可变不可变做了很不同的使用方式。
3.isize 和 usize 两种整数类型是用来衡量数据大小的,它们的位长度取决于所运行的目标平台,如果是 32 位架构的处理器将使用 32 位位长度整型。
C语言代码:
// csrc/cfoo1.c
#include
#include
#include
typedef struct Students {
int num; // serial number
int total; // total score
} Student;
extern void fill_students(Student *stu, int);
extern void print_students(Student *stu, int);
void print_students_c(Student *stu, int n) {
int i;
for (i=0; i<n; i++) {
printf("C side print: %d %d\n", stu[i].num, stu[i].total);
}
}
void main() {
int len = 10;
Student students[len];
// call rust fill and print functions
fill_students(students, len);
print_students(students, len);
// call c print function
print_students_c(students, len);
}
makefile代码:
GCC_BIN ?= $(shell which gcc)
CARGO_BIN ?= $(shell which cargo)
run: clean build
./main
clean:
$(CARGO_BIN) clean
rm -f ./main
build:
$(CARGO_BIN) build
$(GCC_BIN) -o ./main ./src/main.c -Isrc -L. -l:./target/debug/libnew_test.so
C 中分配堆内存,交由 Rust 端填充,并且两边分别打印。
这里的Rust代码和上面的一样,主要是C语言代码如下:
// csrc/cfoo2.c
#include
#include
#include
typedef struct Students {
int num; // serial number
int total; // total score
} Student;
extern void fill_students(Student *stu, int);
extern void print_students(Student *stu, int);
Student* create_students(int n) {
if (n <= 0) return NULL;
Student *stu = NULL;
stu = (Student*) malloc(sizeof(Student)*n);
return stu;
}
void release_students(Student *stu) {
if (stu != NULL)
free(stu);
}
void print_students_c(Student *stu, int n) {
int i;
for (i=0; i<n; i++) {
printf("C side print: %d %d\n", stu[i].num, stu[i].total);
}
}
void main() {
int len = 10;
Student* students = create_students(len);
// call rust fill and print functions
fill_students(students, len);
print_students(students, len);
// call c print function
print_students_c(students, len);
release_students(students);
}
代码解释:
1.这里的传递方式和上面还是一样的,只是这里的C语言部分,自己做了个申请,,定义了一个返回结构体指针的函数,这边的堆释放需要手动完成。
我们从两个示例的对比可以看到,C 这边栈和堆的指针,都可以用相同的 Rust 的代码。也就是说,Rust 这边,它就认 C 的指针,而不管这个指针是从哪里来,栈也好,堆也好,甚至其它地址的指针也好,对 Rust 来说,其实都一样(本质上都是内存指针)。