Rust FFI 编程 - Rust导出共享库05
时间:2022-07-23
本文章向大家介绍Rust FFI 编程 - Rust导出共享库05,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
本章,我们继续来玩耍跨Rust 和 C FFI边界的指针。
设计
本节,我们设计这样一个示例:
示例 1
- Rust 导出一个 so 库
- main 函数在 C 这边,链接 Rust 的 so 库
- C 中分配栈内存,交由 Rust 端填充
- Rust 端打印
- C 端打印
示例 2
- 同样的示例,C 中分配堆内存,交由 Rust 端填充,并且两边分别打印。
下面我们直接看示例1的代码。
示例 1 代码
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);
}
}
记得 Cargo.toml 加上:
[lib]
crate-type = ["cdylib"]
C 端
// csrc/cfoo1.c
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
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 %dn", 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);
}
C 端代码这样编译:
gcc -o ./cfoo1 ./cfoo1.c -L ./ -lrustffi4
(注意,我已经将 cargo build 生成的 librustffi4.so 文件从 target/debug/ 目录拷贝至 C 代码所在目录)
C 端二进制运行:
LD_LIBRARY_PATH=. ./cfoo1
结果如下:
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
可以看到,C的栈空间上分配的结构体数组,已经被Rust这边成功填充了。
感觉已经没什么可讲的了。看过本教程之前内容的同学,应该会秒懂。
接着来看示例2的代码。
示例2的代码
示例2的代码,Rust 这边没有变化。下面直接看 C 这边的代码:
// csrc/cfoo2.c
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
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 %dn", 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);
}
C 端代码这样编译:
gcc -o ./cfoo2 ./cfoo2.c -L ./ -lrustffi4
(注意,我已经将 cargo build 生成的 librustffi4.so 文件从 target/debug/ 目录拷贝至 C 代码所在目录)
C 端二进制运行:
LD_LIBRARY_PATH=. ./cfoo2
结果如下:
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
可以看到,两个示例打印结果完全一致。
示例2的C语言这边是在堆上 malloc 了一块内存,所以程序结束的时候,要记得 free 掉。
我们从两个示例的对比可以看到,C 这边栈和堆的指针,都可以用相同的 Rust 的代码。也就是说,Rust 这边,它就认 C 的指针,而不管这个指针是从哪里来,栈也好,堆也好,甚至其它地址的指针也好,对 Rust 来说,其实都一样(本质上都是内存指针)。
结论
本章通过构造两个示例,演示了 Rust导出共享库 的一个操作场景,例子清晰明了,可细品。
本章示例的所有代码,皆可在:https://github.com/daogangtang/learn-rust/tree/master/11rustffi4 找到。
- WordPress 开发之让浏览器自动加载最新的CSS、JS文件(免刷新缓存)
- [C#6] 0-概览
- 移植Windows自宿主WCF服务到Linux/Mono2.8
- IISWeb应用防火墙WAF
- WordPress 开发之让浏览器自动加载最新的CSS、JS文件(免刷新缓存)
- 在Windows 7无法通过UNC路径连接到共享文件夹的解决方法
- SQL Sever索引
- 使用SQL Server Management Studio 2008 将数据库里的数据导成脚本
- jquery mobile 移动web(5)
- [C#6] 1-using static
- WordPress 中的 Debug 模式(调试模式)
- Windows Server 2008 R2 Server Core 的 Microsoft .NET Framework 4安装程序
- [C#6] 4-string 插值
- 使用API Key验证WCF Data Service
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android开发实现读取Assets下文件及文件写入存储卡的方法
- Android studio实现刮刮乐的方法
- Android studio圆形进度条 百分数跟随变化
- Android中的SpannableString与SpannableStringBuilder详解
- 浅谈Android 中图片的三级缓存策略
- Android主线程和子线程区别详解
- Android 用RxBinding与RxJava2实现短信验证码倒计时功能
- Android右滑返回上一个界面的实现方法
- Android pull解析xml的实现方法
- Android实现QQ图片说说照片选择效果
- 一个简单的toolabar结合drawlayout使用方法
- 利用DrawerLayout和触摸事件分发实现抽屉侧滑效果
- Android App端与PHP Web端的简单数据交互实现示例
- Android开发实现读取assets目录下db文件的方法示例
- Android Textview实现颜色渐变滚动效果