Rust FFI 编程 - Rust导出共享库06

2024-06-23 00:08
文章标签 rust 共享 编程 导出 06 ffi

本文主要是介绍Rust FFI 编程 - Rust导出共享库06,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本节主要介绍 Rust 导出共享库时,如何通过指针在 Rust 和 C 之间传递结构体。上一节的示例是结构体的内存在 C 端分配,本节介绍内存在 Rust 这边分配,由 C 填充和使用。

设计

本节的示例:

  • Rust 中导出共享库,包含三个函数:

    • student_new,Rust 端分配内存并用默认值初始化,由 C 端填充和更新;

    • student_alice,Rust 端分配内存并初始化,由 C 端使用;

    • student_free,供 C 端用来释放结构体的内存

  • C 中定义main函数,链接 Rust 的共享库,并调用相关函数;

实现

工程的初始化类似之前的导出共享库示例,导出.so的共享库要在Cargo.toml中加上:

[lib]
crate-type = ["cdylib"]

下面我们直接看示例的代码。

首先,在 Rust 端和 C 头文件中定义声明要传递的结构体。

// src/lib.rs
#[repr(C)]
#[derive(Debug)]
pub struct Student {pub num: c_int,pub total: c_int,pub name: [c_char; 20],pub scores: [c_float; 3],
}
// src/example_03.h
typedef struct Student
{int num;int total;char name[20];float scores[3];
} Student;

其次在 Rust 端实现我们设计的三个函数。

#[no_mangle]
pub extern "C" fn student_new() -> *mut Student {let new_stu: Student = Default::default();Box::into_raw(Box::new(new_stu))
}#[no_mangle]
pub extern "C" fn student_alice() -> *mut Student {let mut init_char_array: [c_char; 20] = [0; 20];for (dest, src) in init_char_array.iter_mut().zip(b"Alice\0".iter()) {*dest = *src as _;}let scores = [92.5, 87.5, 90.0];let alice = Student {num: 001,total: 280,name: init_char_array,scores,};Box::into_raw(Box::new(alice))
}#[no_mangle]
pub extern "C" fn student_free(p_stu: *mut Student) {if !p_stu.is_null() {unsafe {println!("rust side print: {:?}", Box::from_raw(p_stu));Box::from_raw(p_stu)};}
}

其中一个比较有挑战的地方是,要传递的结构体Student中有个固定长度的c_char数组。如何在 Rust 中初始化它并为其赋值呢?

我们知道 Rust 中的c_char表示 C 中的字符char,但 C 的char类型(表示一个整数)完全不同于 Rust 的char类型(表示一个 Unicode 标量值),所以在 Rust 中的c_char实际上是i8/u8

let mut init_char_array: [c_char; 20] = [0; 20];
for (dest, src) in init_char_array.iter_mut().zip(b"Alice\0".iter()) {*dest = *src as _;
}

这里我们通过zip函数一次遍历两个数组,并完成赋值。

接下来我们在 C 的头文件中声明这三个函数,并看看 C 端的调用代码。

// csrc/main.cint main(void) {Student *c_ptr = student_alice();printf("Student Num: %d\t Total: %d\t Name: %s\t\n", c_ptr->num, c_ptr->total, c_ptr->name);student_free(c_ptr);Student *stu = student_new();printf("Before fill data: Student Num: %d\t Total: %d\t Name: %s\t Scores: %.1f\t%.1f\t%.1f\n",stu->num, stu->total, stu->name,stu->scores[0], stu->scores[1], stu->scores[2]);stu->num = 2;stu->total = 212;strcpy(stu->name, "Bob");stu->scores[0] = 60.6;stu->scores[1] = 70.7;stu->scores[2] = 80.8;printf("After fill data: Student Num: %d\t Total: %d\t Name: %s\t Scores: %.1f\t%.1f\t%.1f\n",stu->num, stu->total, stu->name,stu->scores[0], stu->scores[1], stu->scores[2]);student_free(stu);return 0;
}

我们编写Makefile,编译并运行。结果如下:

➜  example_03 git:(master) ✗ make
/data/cargo/bin/cargo clean
rm -f ./csrc/main
/data/cargo/bin/cargo build --releaseCompiling example_03 v0.1.0 (/data/github/lester/rust-practice/ffi/example_03)Finished release [optimized] target(s) in 0.39s
/usr/bin/gcc -o ./csrc/main ./csrc/main.c -Isrc -L. -l:target/release/libexample_03.so
./csrc/main
Student Num: 1   Total: 280      Name: Alice
rust side print: Student { num: 1, total: 280, name: [65, 108, 105, 99, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], scores: [92.5, 87.5, 90.0] }
Before fill data: Student Num: 0         Total: 0        Name:   Scores: 0.0    0.0     0.0
After fill data: Student Num: 2  Total: 212      Name: Bob       Scores: 60.6   70.7    80.8
rust side print: Student { num: 2, total: 212, name: [66, 111, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], scores: [60.6, 70.7, 80.8] }

我们可以看出,Rust 端分配内存并初始化的结构体,在 C 端可以被完美地使用和更新,要注意的是在调用后,需要调用student_free函数来释放结构体的内存。

结论

本文通过设计一个示例,演示了 Rust 导出共享库时,提供内存分配和释放的函数,通过指针传递结构体,并在 C 端完美实现使用和更新结构体。

本章示例的所有代码:https://github.com/lesterli/rust-practice/tree/master/ffi/example_03。

这篇关于Rust FFI 编程 - Rust导出共享库06的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1085791

相关文章

Mac备忘录怎么导出/备份和云同步? Mac备忘录使用技巧

《Mac备忘录怎么导出/备份和云同步?Mac备忘录使用技巧》备忘录作为iOS里简单而又不可或缺的一个系统应用,上手容易,可以满足我们日常生活中各种记录的需求,今天我们就来看看Mac备忘录的导出、... 「备忘录」是 MAC 上的一款常用应用,它可以帮助我们捕捉灵感、记录待办事项或保存重要信息。为了便于在不同

MySQL Workbench工具导出导入数据库方式

《MySQLWorkbench工具导出导入数据库方式》:本文主要介绍MySQLWorkbench工具导出导入数据库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录mysql Workbench工具导出导入数据库第一步 www.chinasem.cn数据库导出第二步

Java如何根据word模板导出数据

《Java如何根据word模板导出数据》这篇文章主要为大家详细介绍了Java如何实现根据word模板导出数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... pom.XML文件导入依赖 <dependency> <groupId>cn.afterturn</groupId>

rust 中的 EBNF简介举例

《rust中的EBNF简介举例》:本文主要介绍rust中的EBNF简介举例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. 什么是 EBNF?2. 核心概念3. EBNF 语法符号详解4. 如何阅读 EBNF 规则5. 示例示例 1:简单的电子邮件地址

C#实现高性能Excel百万数据导出优化实战指南

《C#实现高性能Excel百万数据导出优化实战指南》在日常工作中,Excel数据导出是一个常见的需求,然而,当数据量较大时,性能和内存问题往往会成为限制导出效率的瓶颈,下面我们看看C#如何结合EPPl... 目录一、技术方案核心对比二、各方案选型建议三、性能对比数据四、核心代码实现1. MiniExcel

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能

《POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能》ApachePOI是一个流行的Java库,用于处理MicrosoftOffice格式文件,提供丰富API来创建、读取和修改O... 目录前言:Apache POIEasyPoiEasyExcel一、EasyExcel1.1、核心特性

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的