Skip to content

Commit d038abd

Browse files
authored
update some items and add new items : p.uns.ffi.16-17-18 (#116)
* V 0.3 * V0.3: modify README * update Version to 0.3 * add code example for P.SEC.01 * modify P.SEC.01 * 3.30 review * update * update to 1.0 beta * fixed for P.NAM.01 * Improvement desc for P.NAM.05 && P.NAM.07 && P.UNS.MEM.04 * fixed code typo * update some items and add new items : p.uns.ffi.16-17-18
1 parent 01d5bd3 commit d038abd

File tree

13 files changed

+127
-6
lines changed

13 files changed

+127
-6
lines changed

src/SUMMARY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@
294294
- [P.UNS.FFI.13 自定义数据类型要保证一致的数据布局](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.13.md)
295295
- [P.UNS.FFI.14 在 FFi 中使用的类型应该拥有稳定布局](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.14.md)
296296
- [P.UNS.FFI.15 从外部传入的不健壮类型的外部值要进行检查](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.15.md)
297+
- [P.UNS.FFI.16 给 C 接口传递 Rust 闭包时需要将数据和代码进行分离,并要保证其安全性](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.16.md)
298+
- [P.UNS.FFI.17 当Rust绑定C-API不透明(Opaque)类型时,应该使用指向专用不透明类型的指针而不是`c_void`指针](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.17.md)
299+
- [P.UNS.FFI.18 避免将 trait 对象传递给 C 接口](./safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.18.md)
297300
- [I/O](./safe-guides/coding_practice/unsafe_rust/io.md)
298301
- [P.UNS.FIO.01 在使用原始句柄的时候,要注意 `I/O` 安全性](./safe-guides/coding_practice/unsafe_rust/io/P.UNS.FIO.01.md)
299302
- [Unsafe 代码术语指南](./safe-guides/coding_practice/unsafe_rust/glossary.md)

src/safe-guides/code_style/comments/P.CMT.05.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**【描述】**
44

5-
通过在注释中开启 `FIXME``TODO` 可以方便协作。
5+
通过在注释中开启 `FIXME``TODO` 可以方便协作。正式发布版本可以不做此类标注。
66

77
注意:此条目不适于使用 `rustfmt`相关配置项 `report_fixme``report_todo`,在 `rustfmt` v2.0 中已经移除这两项配置。
88

src/safe-guides/code_style/fmt/P.FMT.11.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@
1313
1414
**【反例】**
1515

16+
例1:
17+
1618
```rust
1719
// 不符合: 同一模块类型应该置于同一个块内
1820
// 当 `imports_granularity="Preserve"`
1921
use foo::b;
2022
use foo::b::{f, g};
2123
use foo::{a, c, d::e};
2224
use qux::{h, i};
25+
```
2326

27+
例2:
28+
29+
```rust
2430
// 不符合:当按默认值设置时,模块导入比较乱,影响可读性
2531
use super::update::convert_publish_payload;
2632
use chrono::Utc;
@@ -40,6 +46,8 @@ use core::f32;
4046

4147
**【正例】**
4248

49+
例1:
50+
4351
```rust
4452
// 符合
4553
// 当 `imports_granularity="Crate"`
@@ -50,7 +58,11 @@ use foo::{
5058
d::e,
5159
};
5260
use qux::{h, i};
61+
```
5362

63+
例2:
64+
65+
```rust
5466
// 符合
5567
// 当 `group_imports="StdExternalCrate` 且 `reorder_imports=true`
5668
use alloc::alloc::Layout;

src/safe-guides/coding_practice/consts/G.CNS.02.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## G.CNS.02 不应断言常量布尔类型
22

3-
**【级别】** 要求
3+
**【级别】** 建议
44

55
**【描述】**
66

src/safe-guides/coding_practice/data-type/G.TYP.02.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
```rust
1212
#![warn(clippy::default_numeric_fallback)]
13-
// 符合
13+
// 不符合
1414
let i = 10; // i32
1515
let f = 1.23; // f64
1616
```

src/safe-guides/coding_practice/data-type/char/G.TYP.CHR.03.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ let x = std::char::from_u32(x);
2727
assert_eq!('%', x);
2828

2929
// 符合:如果确定该整数对应合法的 unicode,可以使用 uncheck 方法加速
30-
let x = std::char::from_u32_unchecked(x);
30+
let x = unsafe {std::char::from_u32_unchecked(x) };
3131
assert_eq!('%', x);
3232
```
3333

src/safe-guides/coding_practice/unsafe_rust/ffi/P.UNS.FFI.10.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
**【正例】**
88

99
```rust
10+
#[no_mangle]
1011
pub extern "C" fn nic_udrv_suspend() {
1112
NIC_ENTITY.try_borrow_mut().suspend(); // suspend()需要可变引用
1213
}
1314

1415
// 对外被 C 调用的接口
16+
#[no_mangle]
1517
pub extern "C" fn nic_udrv_buf_recycle(buf_id: usize) {
1618
NIC_ENTITY.try_borrow().buf_recycle(buf_id); // buf_recycle()内有锁可以避免多线程竞争
1719
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# P.UNS.FFI.16 给 C 接口传递 Rust 闭包时需要将数据和代码进行分离,并要保证其安全性
2+
3+
## 【描述】
4+
5+
在 Rust 中,闭包只是一种语法糖,其实质是由编译器生成的匿名结构体和一些 `call`方法组成。而 C 语言中只支持函数指针,并不支持 Rust 这种闭包。
6+
7+
因此为 C 接口传递 Rust 闭包的思路是将闭包“拆分”为数据(匿名结构体的实例)和函数(`call()`方法)部分来将闭包传递给 C 接口,并且要从以下三方面保证安全性:
8+
9+
1. Rust 闭包中捕获变量引用的生命周期有效性。
10+
2. 传入的Rust 闭包要实现 `std::panic::UnwindSafe`,这样可以保证异常安全
11+
3. 传入的 Rust 闭包要实现 `Send`,这样可以保证线程安全
12+
13+
14+
## 【正例】
15+
16+
```rust
17+
// Safety: 此处需要保证以下不变性来进行安全抽象
18+
// - widget 必须是一个有效的指针
19+
// - 这里因为使用 Rust 引用,所以它一定是有效的
20+
// - 数据必须在其被析构之前有效
21+
// - 这里增加了`'static` 限定来确保它有效
22+
// - 增加 `std::panic::UnwindSafe` 和 `Send` 限定,确保其异常安全和线程安全
23+
fn register_c_callback<F>(widget: &mut ffi::widget_t, callback: F)
24+
where
25+
F: FnMut(ffi::event_t) + 'static + std::panic::UnwindSafe + Send,
26+
{
27+
// 需要闭包实例数据保留一定时间,所以将其放到堆上,使用`Box::into_raw`防止其被析构
28+
let data = Box::into_raw(Box::new(callback));
29+
unsafe {
30+
// 分别将 数据 和 闭包调用代码 传入 C 接口
31+
ffi::widget_register_callback(
32+
widget,
33+
data as *mut _, // 数据
34+
call_closure::<F>, // 代码
35+
drop_box::<F>,
36+
);
37+
}
38+
}
39+
40+
// Safety: 传入该函数的指针必须是 `F` 类型的非空指针
41+
// 这里不需要 `#[no_mangle]`,是因为它会以函数指针的方式直接传递给 C ,而不需要通过函数名称调用
42+
unsafe extern "C" fn call_closure<F>(
43+
data: *mut libc::c_void,
44+
event: ffi::event_t,
45+
)
46+
where
47+
F: FnMut(ffi::event_t) + 'static + std::panic::UnwindSafe + Send,
48+
{
49+
let callback_ptr = data as *mut F;
50+
let callback = &mut *callback_ptr;
51+
callback(event); // 调用闭包
52+
}
53+
54+
// 在 C 端手动调用的析构函数
55+
unsafe extern "C" fn drop_box<T>(data: *mut libc::c_void) {
56+
Box::from_raw(data as *mut T);
57+
}
58+
```
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# P.UNS.FFI.17 当Rust绑定 C-API 不透明(Opaque)类型时,应该使用指向专用不透明类型的指针而不是`c_void`指针
2+
3+
## 【描述】
4+
5+
使用专门构建的不透明类型相比于直接使用 `c_void`可以提供一定程度的类型安全性。
6+
7+
## 【正例】
8+
9+
C 库中包含了一个不透明类型的 foo 指针和 bar 指针:
10+
11+
```c
12+
void foo(void *arg);
13+
void bar(void *arg);
14+
```
15+
16+
通过包含私有字段`_private`且不包含构造函数,创建了两个无法在此模块之外实例化的不透明类型。空数组既是零大小又可设置布局为`#[repr(C)]`。
17+
18+
```rust
19+
#[repr(C)]
20+
pub struct Foo {_private: [u8; 0]}
21+
22+
#[repr(C)]
23+
pub struct Bar {_private: [u8; 0]}
24+
25+
26+
// SAFETY:
27+
// 因为 Foo 和 Bar类型不同,所以将在它们两者之间获得类型安全,这样就不可能意外地传递一个指向 `bar()` 的`Foo`指针。
28+
extern "C" {
29+
fn foo(arg: *mut Foo);
30+
fn bar(arg: *mut Bar);
31+
}
32+
```
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# P.UNS.FFI.18 避免将 trait 对象传递给 C 接口
2+
3+
## 【描述】
4+
5+
Rust 中的多态性主要由 trait 来提供。但是在 FFi 时,将 Rust trait 对象传递给 C 接口,并不能保证 FFi 安全。因为 Rust trait 对象没有稳定的 ABI,所以我们不能通过 `Box<dyn Trait>` 值传递越过 FFI 边界。
6+
7+
所以,最好的方式是不要在 FFi 时通过传递 trait对象来使用多态性。
8+
9+
> 如果必须要在 FFi 中使用多态性,有以下几种方式:
10+
> 1. 使用枚举。像 C 传递一个指向枚举的指针。
11+
> 2. 使用 [`thin_trait_object`](https://github.com/kotauskas/thin_trait_object) 模式,是 FFi 安全的。

0 commit comments

Comments
 (0)