Rust FFI 编程 - Rust 语言层面对 FFI 的支持
Rust FFI 编程 - Rust 语言层面对 FFI 的支持
MikeLoveRust 2020-04-16 18:24:06 253 收藏
版权
Rust 语言对 FFI 有比较完善的支持。本节主要讲在基础设施层面,Rust 语言对 FFI 的支持。
Rust 语言主要在关键字和标准库两个方面对 FFI 提供了支持,具体如下:
关键字 extern属性 #[no_mangle]外部块 ExternBlock 及其属性 link 和 link_name标准库std:os:raw 模块std:ffi 模块
- 关键字 extern
在 Rust 语言中,使用关键字extern可以实现 Rust 语言与其它语言的交互,这是 Rust 外部函数接口 FFI 的基础。
1.1 extern 函数
直接在 Rust 的函数关键字fn前使用关键字extern,可以创建一个允许其他语言调用 Rust 函数的接口。
同时可以通过使用 ABI 字符串[1]来指定具体的 ABI,其中有三个 ABI 字符串是跨平台的:
extern "Rust",默认的 ABI,在 Rust 代码中对任何普通函数fn foo()声明时都将使用该 ABI。extern "C",指定使用 C-ABI,类似extern fn foo(),无论 C 编译器支持哪种默认设置。extern "system",通常类似extern "C",但在 Win32 平台上,它是"stdcall",或用于链接到 Windows API。// ffi/c-call-rust/src/lib.rspub extern "C" fn call_from_rust() {println!("This is a Rust function for C!");
}
1.2 属性 #[no_mangle]
属性no_mangle,用来关闭 Rust 的名称修改(name mangling)功能。Mangling 是编译器在解析名称时,修改我们定义的函数名称,增加一些用于其编译过程的额外信息。
但在与其它语言交互时,如果函数名称被编译器修改,程序开发者无法知道修改后的函数名称,其它语言也无法按原名称调用。执行1.1中示例代码时,报错信息如下:
Undefined symbols for architecture x86_64:"_call_from_rust", referenced from:_main in main-77cb59.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1
所以为了使 Rust 函数能在其它语言中被调用,必须禁用 Rust 编译器的名称修改功能。通过在1.1的示例代码中增加属性 #[no_mangle] ,告诉 Rust 编译器不要修改此函数的名称。
// ffi/c-call-rust/src/lib.rs
#[no_mangle]
pub extern "C" fn call_from_rust() {println!("This is a Rust function for C!");
}
1.3 外部块 ExternBlock
在 Rust 语言中,使用关键字extern可以声明一个外部块(ExternBlock),通过外部块的形式,可以在 Rust 代码中调用外部代码。
在 Rust 语言参考文档中,使用关键字extern声明一个外部块的语法格式如下:
extern Abi? {InnerAttribute*ExternalItem*
}
其中的Abi表示调用库使用的 ABI 标准,可选值为1.1节中提到的 ABI 字符串。缺省情况下,外部块默认为标准的 C-ABI。在定义外部块的时候,可以使用 link和 link_name 这两个属性,通过它们来控制外部块的行为。
外部块的属性 link
属性link用来指定原生库的名称,编译器根据它为外部块链接原生库。它支持的键有:name,kind和wasm_import_module,name用来定义要链接的原生库的名称。 kind是一个可选值,通过它来指定原生库的类型,它有以下三种可选的值:
dylib,表示为动态库。如果未指定kind,则它为默认值。static,表示为静态库。framework,表示 macOS 的框架,这仅对 macOS 目标有效。
如果对属性link设定了原生库的类型kind,则必须包括原生库的名称name。
wasm_import_module可用于指定 WebAssembly 模块的名称,如果未指定wasm_import_module,则模块名称默认为env。
#[link(name = "c_library")]
extern "C" {fn c_function(input: i32) -> i32;
}
外部块的属性 link_name
在外部块内,通过属性link_name,指定原生库中函数或静态对象的名称,编译器根据它可以为外部块链接原生库并导入该名称定义的函数或静态对象。
extern "C" {#[link_name = "c_function_name"]fn name_in_rust();
}
外部块中声明的函数在 Rust 代码中是不安全的,因为其他语言不会强制执行 Rust 语言中的语法规则,故无法检查这些代码,所以程序开发者务必要确保这部分代码的安全。
// ffi/rust-call-c/src/main.rs
// 标准库<stdlib.h>内置的abs函数
extern "C" {#[link_name = "abs"]fn abs_in_rust(input: i32) -> i32;
}fn main() {unsafe {println!("abs(-1) is {}", abs_in_rust(-1));}
}
- 标准库
在实际开发 Rust 语言与其它语言相互调用的程序时,会遇到需要相互传递参数的情况。Rust 标准库std::os::raw 和std::ffi 这两个模块提供了这方面的支持。
2.1 std::os::raw 模块
使用 FFI 进行交互的代码通常会使用到 C 语言提供的基本类型,标准库 std::os::raw 模块[2]提供了一些类型与 C 语言定义的类型相匹配,以便与 C 语言交互的代码引用正确的类型。
类型
解释
c_char
等同于 C 语言的char类型
c_double
等同于 C 语言的double类型
…
…
更多类型可以查见参考链接[2]。
2.2 标准库 std::ffi 模块
由于 Rust 语言中字符串与 C 语言字符串的不同之处,标准库 std::ffi 模块[3]提供了一组实用的程序,主要用于外部函数接口 FFI 的绑定,以及用在与其他语言传递类 C 字符串的代码中。
在支持 C-ABI 的语言(如:Python)中传递 UTF-8 字符串[4]时,CString和CStr很有用。
CStr
在 C 语言中生成的字符串,Rust 使用CStr来表示,它和str类型对应,表明并不拥有这个字符串的所有权。所以CStr表示一个以终止符\n结尾的字节数组的引用,如果它是有效的 UTF-8 字符串,则可以将其转换为 Rust 语言中的&str。实现从 C 语言到 Rust 语言的字符串传递。
CString
在 Rust 语言中生成的字符串,Rust 使用CString来表示用以传给 C 程序的字符串。CString以终止符\n结尾,并且没有内部\n字符,代码可以首先从 Rust 语言的普通字符串创建CString类型,然后将其作为参数传递给使用 C-ABI 约定的字符串函数。实现从 Rust 语言到 C 语言的字符串传递。
use std::ffi::CString;
use std::os::raw::c_char;#[no_mangle]
pub extern rust_printer(input: *const c_char) {let mut hello = String::from("Hello World!");let c_str_to_print = CString::new(hello).unwrap();
}
注意:因为所有权概念是 Rust 语言特有的,所以在和 C 语言交互时,必须实现一个释放内存的方法供 C 代码调用。
此外在不同操作系统平台传输字符串,或者在捕获外部命令的输出时,OsString和OsStr很有用。
OsString表示传递给操作系统的拥有所有权的字符串。例如,env::var_os()用于查询环境变量,它返回一个Option<OsString>。如果环境变量存在,将获得Some(os_string),然后可以将其转换为 Rust 字符串。OsStr表示传递给操作系统的字符串引用,可以按照与OsString类似的方式将其转换为 UTF-8 编码的 Rust 字符串切片。
另外,当用作指针时,std::ffi::c_void等同于 C 语言中的void类型。
示例代码:https://github.com/lesterli/rust-practice/tree/master/ffi
参考链接
[1] 外部块支持的 ABI 字符串,https://doc.rust-lang.org/reference/items/external-blocks.html
[2] 标准库 std::os::raw 模块,https://doc.rust-lang.org/stable/std/os/raw/index.html
[3] 标准库 std::ffi 模块,https://doc.rust-lang.org/std/ffi/index.html
[4] Rust 中 String 与 UTF-8 编码,https://mp.weixin.qq.com/s/ZX_0G6JcNMusLz6JJOkNSg
Rust FFI 编程 - Rust 语言层面对 FFI 的支持相关推荐
- Rust FFI 编程--理解不同语言的数据类型转换
1. 简介 "FFI"是" Foreign Function Interface"的缩写,大意为不同编程语言所写程序间的相互调用.鉴于C语言事实上是编程语言界的 ...
- Rust FFI 编程 - Bindgen 工具介绍
前面我们经历了<Rust FFI 编程 - 基础知识>.<Rust FFI 编程 - 手动绑定 C 库>和<Rust FFI 编程 - Rust 导出共享库>三个大 ...
- Rust中FFI编程知识点整理总结
Rust语言对FFI的支持 Rust 语言主要在关键字和标准库两个方面对 FFI 提供了支持,具体如下: 关键字 extern 属性 #[no_mangle] 外部块 ExternBlock 及其属性 ...
- Rust FFI 与C语言互相调用
Rust FFI 与C语言互相调用 参考 cbindgen 简介 二进制方式构建 脚本构建 Demo程序说明 示例工程: makefile test脚本 基本数据类型 Rust侧 C侧 对象 Rust ...
- flutter/dart通过ffi调用rust代码
ffi简介 FFI(Foreign Function Interface)是用来与其它语言交互的接口,在有些语言里面称为语言绑定(language bindings),Java 里面一般称为 JNI( ...
- Rust应用调用C语言动态库
外部功能接口FFI 虽然高级(脚本)编程语言的功能丰富,表达能力强,但对底层的一些特殊操作的支持并不完善,就需要以其他编程语言来实现.调用其他编程语言的接口,被称为Foreign Function I ...
- rust异步编程--理解并发/多线程/回调/异步/future/promise/async/await/tokio
1. 异步编程简介 通常我们将消息通信分成同步和异步两种: 同步就是消息的发送方要等待消息返回才能继续处理其它事情 异步就是消息的发送方不需要等待消息返回就可以处理其它事情 很显然异步允许我们同时做更 ...
- 为什么说 Rust 是编程的未来?
作者 | Scalac 译者 | 弯月 出品 | CSDN(ID:CSDNnews) 2020年 Stack Overflow 的调查报告显示,Rust 名列最受欢迎编程语言的榜首,86% 的开发人员 ...
- rust编程语言排名_Rust语言在堆栈溢出调查中排名最高
rust编程语言排名 Rust在Stack Overflow的开发人员调查中连续第五年获得"最受欢迎的"编程语言的第一名,而Python从第二名滑落到第三名,仅次于TypeScri ...
- LLVM:Rust、Clang等语言的强大支持以及编译原理和过程
新的开发语言如雨后春笋般涌现,比如 Mozilla 的 Rust.Apple 的 Swift 以及 Jetbrains 的 Kotlin 等等,当然很多好的语言也在不断迭代,比如 Java.这些语言为 ...
最新文章
- EOJ_1082_Virtual Friends
- [转]两个经典的windbg调试案例,值得学习。
- Lock和synchronized的选择
- MySQL运维系列 之 如何监控大事务
- [转载] python循环中break、continue 、exit() 、pass的区别
- Linux实用代码--文件系统操作
- c语言设置输出字符大小_C语言中常用的几个头文件及库函数
- 最新安卓手机性价比榜公布:Redmi连夺三冠
- 计算机二级MS office之excel常用函数
- 多种方法对网页文字进行快速复制(仅供学习使用,勿践踏他人成果)
- linux top命令 什么意思,Linux下的top命令、%cpu和cps(s)到底是什么意思呢!
- 数据结构之队列的应用-超好玩的汽车加油站模拟器(C语言)
- 关键字: CCTV5 天下足球 盗版
- Python案例—AQI 空气质量指数
- 5G发展面临窘境,5G手机销量环比下滑,手机企业对5G热情降温
- 蛋花花:人工智能写的诗版权到底算谁的
- 用DapperExtensions和反射来实现一个通用搜索
- 常见html的标题含义(1)
- xcode SVN 上传代码只能用SVN client 或者SVN 命令行
- [hadoop全分布部署]安装Hadoop、配置Hadoop 配置文件①