返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >Rust中FFI编程知识点整理总结(推荐)
  • 288
分享到

Rust中FFI编程知识点整理总结(推荐)

2024-04-02 19:04:59 288人浏览 安东尼
摘要

目录Rust语言对FFI的支持libc-crate库cbindgen 工具的介绍和使用Rust指针1.Rust自带的指针类型:2. 原始指针3.智能指针Rust和C交互时的各种指针变

Rust语言对FFI的支持

Rust 语言主要在关键字和标准库两个方面对 FFI 提供了支持,具体如下:
关键字 extern
属性 #[no_mangle]
外部块 ExternBlock 及其属性 link 和 link_name
标准库
std:os:raw 模块:例如c_char。
std:ffi 模块:传递 UTF-8 字符串时,CString和CStr很有用。

libc-crate库

你可以使用 libc::foo 这种形式访问这个库中的任何导出内容。
在Rust里,只能创建子线程,如果想创建子进程,就需要用到libc库

fn main() {    
unsafe {        
let pid = libc::fork();                                                                                                               if pid > 0 {println!("Hello, I am parent thread: {}", libc::getpid());}   
else if pid == 0 {println!("Hello, I am child thread: {}", libc::getpid());println!("My parent thread: {}", libc::getppid());        }        
else {println!("Fork creation failed!");}}}

1.libc 的所有函数调用,都必须放进 unsafe 块中。因为它的所有调用都是 unsafe 的;
2.std 的线程操作封装,好用,形象。libc 的进程操作,与 C 语言系统编程一样,完全是另外一套思路和编程风格;
3.std 的线程操作虽然简洁,但是也缺少更细颗粒度的控制。而 libc 可以对进程的操作(及后面对子进程的功能扩充,父进程中的信号管理等),做到完全的控制,更加灵活,功能强大;
4.std 本身无法实现进程 fork 的功能。
因为我 Rust 的封装是 zero cost (零成本)的。零成本抽象赋予了 Rust 系统编程的能力。

libc 与 std::os:?::raw,这里面有的用法是一样的,没有任何问题。简单的和C交互可以用os:raw里面的,而一旦产生了系统调用或者 Unix 环境编程,那么就得引入 libc 库来操作。

cbindgen 工具的介绍和使用

这个工具就是将写好的Rust代码配置一下,然后会自动生成接口代码头文件等等。其实,FFI封装、转换,熟悉了之后,知识点就那些,模式也比较固定,如果接口量很大,那就需要大量重复的 coding。量一大,人手动绑定出错的机率也大。所以这种辅助工具的意义就显露出来了。基于辅助工具生成的代码,如不完美,再适当手动修一修,几下就能搞定,大大提高生产效率。

Rust指针

在Rust中,存在三种类型的指针:

1.Rust自带的指针类型:

引用—安全的指针

&T:它是对类型T的不可变引用
&mut T:它是对类型T的可变引用

2. 原始指针

众所周知,Rust语言的指针是一种安全的指针,它会遵循一定的规则,比如ownership规则,会确保不出现悬挂指针。但是当我们需要写一些底层框架的时候,往往需要绕过这些规则,自由的控制指针,这时候我们就可以使用原始指针。
*const T:表示指向类型T的不可变原始指针。它是Copy类型。这类似于&T,只是它可以为空值。
*mut T:一个指向T的可变原始指针,它不支持Copy特征(non-Copy)。
以下可以定义Rust的原始指针:

fn main() {
    let mut num = 5;
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;
}

3.智能指针

管理原始指针非常不安全,开发者在使用它们时需要注意很多细节。不恰当地使用它们可能会以非常隐蔽的方式导致诸如内存泄漏、引用挂起,以及大型代码库中的双重释放等问题。为了解决这些问题,我们可以使用c++中广泛采用的智能指针。
智能指针的两个特性:Drop和Deref
Drop:这是多次提及的特征,它可以自动释放相关值超出作用域后占用的资源。Drop特征类似于你在其他语言中遇到的被称为对象析构函数的东西。它包含一个drop方法,当对象超出作用域时就会被调用。该方法将&mut self作为参数。使用drop释放值是以LIFO的方式进行的。也就是说,无论最后构建的是什么,都首先会被销毁。drop方法是你为自己的结构体放置清理代码的理想场所。例如使用引用计数值或GC时,它尤其方便。当我们实例化任何Drop实现值时(任意堆分配类型),Rust编译器会在编译后的代码中每个作用域结束的位置插入drop方法调用。因此,我们不需要在这些实例上手动调用drop方法。
Deref:为了提供与普通指针类似的行为,也就是说,为了能够解引用被指向类型的调用方法,智能指针类型通常会实现Deref特征,这允许用户对这些类型使用解引用运算符*。虽然Deref只为你提供了只读权限,但是还有DerefMut,它可以为你提供对底层类型的可变引用。

智能指针的种类:
标准库中的智能指针有如下几种。
Box:它提供了最简单的堆资源分配方式。Box类型拥有其中的值,并且可用于保存结构体中的值,或者从函数返回它们。
Rc:它用于引用计数。每当获取新引用时,计数器会执行递增操作,并在用户释放引用时对计数器执行递减操作。当计数器的值为零时,该值将被移除。
Arc:它用于原子引用计数。这与之前的类型类似,但具有原子性以保证多线程的安全性。
Cell:它为我们提供实现了Copy特征的类型的内部可变性。换句话说,我们有可能获得多个可变引用。
RefCell:它为我们提供了类型的内部可变性,并且不需要实现Copy特征。它用于运行时的定以确保安全性。

引用计数指针:
所有权规则只允许某个给定作用域中存在一个所有者。但是,在某些情况下你需要与多个变量共享类型。例如在GUI库中,每个子窗体小部件都需要具有对其父容器窗口小部件的引用,以便基于用户的resize事件来调整子窗口的布局。虽然有时生命周期允许你将父节点存储为&'a Parent,但是它通常受到’a值生命周期的限制,一旦作用域结束,你的引用将失效。在这种情况下,我们需要更灵活的方法,并且需要使用引用计数类型。程序中的这些智能指针类型会提供值的共享所有权。

引用计数类型支持某个粒度级别的垃圾回收。在这种方法中,智能指针类型允许用户对包装值进行多次引用。在内部,智能指针使用引用计数器(这里是refcount)来统计已发放的并且活动的引用数量,不过它只是一个整数值。当引用包装的智能指针值的变量超出作用域时,refcount的值就会递减。一旦该对象的所有引用都消失,refcount的值也会变成0,之后该值会被销毁。这就是引用计数指针的常见工作模式。

Rust为我们提供了两种引用计数指针类型。
Rc:这主要用于单线程环境。
Arc:这主要用于多线程环境。

Rust和C交互时的各种指针变换

1.pub extern “C” fn sum_of_array(array: *const u32, len: usize) -> u32
slice::from_raw_parts(array,len)
C端传来的数组(指针类型),进到Rust这边进行强制类型转换,变成非可变原始指针类型。函数slice::from_raw_parts(array,len)就是对原始指针进行转换为Rust切片类型,切片就是一个指针+一个长度即可。

2.CStr::from_ptr(raw_string):CStr就是C端产生数据,Rust端使用,
只是借用,常用于打印。raw_string是直接从C接过来的可变原始指针。
使用std::ffi::CStr提供的from_ptr方法包装 C 的字符串指针,它基于空字符’\0’来计算字符串的长度,并可以通过它将外部 C 字符串转换为 Rust 的 &str和String

use std::ffi::CStr;
use libc::c_char;
extern {
fn char_func() -> *mut c_char;
}

fn get_string() -> String {
unsafe {
let raw_string: *mut c_char = char_func();
let cstr = CStr::from_ptr(raw_string);
cstr.to_string_lossy().into_owned()
}
}

3.CStr::from_ptr(s).to_string_lossy().into_owned():注意to_string_lossy()的使用:因为在rust中一切字符都是采用utf8表示的而c不是,
因此如果要将c的字符串转换到rust字符串的话,需要检查是否都为有效utf-8字节。

4.CString::new(“Hello, world!”).as_ptr():Cstring是Rust端产生数据,C端进行使用。
as_ptr()就是将RustCString指针类型转化为C的原始指针类型。

5.CString::new(“Hello world!”).into_raw()
使用std::ffi::CString提供的一对方法into_raw和from_raw可以进行原始指针转换,由于将字符串的所有权转移给了调用者,所以调用者必须将字符串返回给 Rust,以便正确地释放内存。
into_raw()和.as_ptr()的作用类似,都是变成原始指针传给C端。
6.CString::from_raw(s)
一般在释放内存的时候使用,C端用完需要Rust端来释放。

7.Box::into_raw(Box::new(new_stu)):其实这里是智能指针和两端堆栈申请有关,into_raw()就是将Rust智能指针变成原始指针。
8.Box::from_raw(p_stu):from_raw():就是将C端传来的p_stu变成Rust智能指针。

数组类型传递

C代码:

  uint32_t sum = sum_of_array(numbers, length);

Rust代码:

pub extern "C" fn sum_of_array(array: *const u32, len: usize) -> u32 {
    let array = unsafe {
        assert!(!array.is_null());
        slice::from_raw_parts(array, len)
    };
   array.iter().sum()
}

这里的参数传递一目了然,array一开始是C过来的指针类型,通过slice::from_raw_parts(array,len)之后,变成一个Rust切片类型,后面用iter进行求和。切片类型就是一个指针和一组数据合在一起组成。

字符串类型

对于C语言来说,字符串有两种,一种是共享的只读字符串 char * ,不能修改。另一种是动态分配的可变字符串 char [],可以修改。
而在Rust里面,字符串是由字符的 UTF-8 编码组成的字节序列。表示的类型有很多种。
字符串则比较复杂,Rust 中的字符串,是一组u8组成的 UTF-8 编码的字节序列,字符串内部允许NULL字节;但在 C 中,字符串只是指向一个char的指针,用一个NULL字节作为终止。
我们需要做一些特殊的转换,在 Rust FFI 中使用std::ffi::CStr,它表示一个NULL字节作为终止的字节数组,可以通过 UTF-8 验证转换成 Rust 中的&str。
CStr:表示以空字符终止的 C 字符串或字节数组的借用,属于引用类型。一般用于和 C 语言交互,由 C 分配并被 Rust 借用的字符串。
CString:表示拥有所有权的,中间没有空字节,以空字符终止的字符串类型。一般用于和 C 语言交互时,由 Rust 分配并传递给 C 的字符串。
下面这段代码,在这里get_string使用CStr::from_ptr从C的char*获取一个字符串,并且转化成了一个String。

fn get_string() -> String {
unsafe {
let raw_string: *mut c_char = char_func();
let cstr = CStr::from_ptr(raw_string);
cstr.to_string_lossy().into_owned()
}
}

和CStr表示从C中来,rust不拥有归属权的字符串相反,CString表示由rust分配,Rust拥有所有权,可以进行修改,用以传给C程序的字符串。

use std::ffi::CString;
use std::os::raw::c_char;
extern {
fn my_printer(s: *const c_char);
}

let c_to_print = CString::new("Hello, world!").unwrap();
unsafe {
my_printer(c_to_print.as_ptr()); // 使用 as_ptr 将CString转化成char指针传给c函数
}

两端分配堆栈,另一端填充打印

到此这篇关于Rust中FFI编程知识点整理总结的文章就介绍到这了,更多相关Rust中FFI编程内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Rust中FFI编程知识点整理总结(推荐)

本文链接: https://www.lsjlt.com/news/167552.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Rust中FFI编程知识点整理总结(推荐)
    目录Rust语言对FFI的支持libc-crate库cbindgen 工具的介绍和使用Rust指针1.Rust自带的指针类型:2. 原始指针3.智能指针Rust和C交互时的各种指针变...
    99+
    2024-04-02
  • React-hooks面试考察知识点汇总小结(推荐)
    目录什么是hooks?解决了什么问题?Hook 简介Hook APIuseState指定初始 state惰性初始化自定义 Hook什么是hooks?解决了什么问题? Hooks 是r...
    99+
    2024-04-02
  • Python面向对象编程知识点总结
    这篇文章主要讲解了“Python面向对象编程知识点总结”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python面向对象编程知识点总结”吧!面向对象编程和函数式编程(面向过程编程)都是程序设...
    99+
    2023-06-16
  • python中的编码知识整理汇总
    问题 在平时工作中,遇到了这样的错误: UnicodeDecodeError: 'ascii' codec can't decode byte 想必大家也都碰到过,很常见 。于是决定对python的...
    99+
    2022-06-04
    知识 python
  • python文件操作相关知识点总结整理
    本文汇总了python文件操作相关知识点。分享给大家供大家参考,具体如下: 总是记不住API。昨晚写的时候用到了这些,但是没记住,于是就索性整理一下吧: python中对文件、文件夹(文件操作函数)的操作需...
    99+
    2022-06-04
    知识点 操作 文件
  • 关于Python面向对象编程的知识点总结
    前言 如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。 接下来我们就来了...
    99+
    2022-06-04
    知识点 面向对象 Python
  • Java中的异常和处理知识点总结
    这篇文章主要介绍“Java中的异常和处理知识点总结”,在日常操作中,相信很多人在Java中的异常和处理知识点总结问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java中的异常和处理知识点总结”的疑惑有所帮助!...
    99+
    2023-06-17
  • python中Task封装协程的知识点总结
    说明 Task是Future的子类,Task是对协程的封装,我们把多个Task放在循环调度列表中,等待调度执行。 Task对象可以跟踪任务和状态。Future(Task是Futrue的子类)为我们提供了异步编程中最终...
    99+
    2022-06-02
    python Task 封装协程
  • vue中对虚拟dom的理解知识点总结
    本质是一个普通的js对象,用于描述视图界面结构的, 在mouted的回调中,可以输出_vnode,  通过图可以知道,_vnode中有以下几个主要的属性: t...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作