2 个版本
0.0.2 | 2024年1月12日 |
---|---|
0.0.1 | 2024年1月11日 |
#21 in #ptr
用于 element-ptr
16KB
393 行
元素指针
此库公开了一个宏,可以简化处理原始指针的过程。
在Rust中使用原始指针时,一个常见的问题是在通过原始指针遍历结构时难以安全地处理它们。一些困难包括
-
通过原始指针遍历结构时创建中间引用是危险的,也可能是不正确的。这就是为什么存在
addr_of!()
的原因。 -
即使有了
addr_of!()
,其重复使用的语法看起来也可能非常糟糕。每次.field
访问都需要像(*ptr).field
这样,而且宏调用本身也造成了大量杂乱。 -
NonNull<T>
的使用非常令人烦恼。它缺少如offset()
这样的方法,并且不能与前面提到的addr_of!()
一起使用。这意味着使用NonNull<T>
的任何东西都必须不断地在普通原始指针和它之间进行转换。 -
没有简洁的方法可以从
*const [T; L]
中获取一个元素的原始指针。正确的方法是ptr.cast::<T>().add(index)
,但指定转换类型很啰嗦,不指定可能会导致编译器错误。
这个crate试图通过一个综合宏来解决这个问题。
示例
以下示例详细说明了宏的基本用法。
use element_ptr::element_ptr;
struct BaseStruct {
first: u32,
second: ChildStruct,
}
struct ChildStruct {
elements: [u32; 10],
}
unsafe fn get_child_element_ptr(
ptr: *const BaseStruct,
index: usize
) -> *const u32 {
element_ptr!(ptr => .second.elements[index])
}
该宏本身使用特殊语法来描述指针应该如何移动。
首先,调用宏并传入基本指针。这可能是指针类型有效的任何表达式。
element_ptr!(ptr => /* ... */ )
接下来,语法
.second
使指针移动到 second
字段的地址。这是最基本的操作类型,可能也是最常用的。也可以使用整数来索引元组。
然后访问另一个字段。注意,在前一个访问之后,指针是 *const ChildStruct
。这意味着 .elements
将访问内部 ChildStruct
中的 elements
字段。因为结构体是按值存储的,所以不需要解引用指针。
然后,使用以下方式访问数组的索引
[index]
宏将静态检查以确保仅在使用可索引的引用类型时使用索引语法。index
可以是任何表达式,而不仅仅是变量名或静态值。
最后,该宏返回最后访问的子元素的指针,在这种情况下,是 second.elements
中十 u32
之一。
安全性
由于以下几个原因,调用此宏始终是 unsafe
-
addr_of!()
,因此.field
访问需要确保结果指针位于同一 分配对象 的范围内。这是与offset()
相同的要求。 -
类似于 #1,
[index]
访问也需要确保结果指针在范围内。因为index
可以是任意表达式,以及切片和数组都可以被索引,所以这不能在编译时断言。 -
此宏支持操作
NonNull<T>
,因此任何偏移量都可能使指针移动到空指针,导致UB。这几乎总是与 #1 相同,因为地址0
永远不在范围内。
语法 & 语义
有多种元素访问类型,每种都可以执行不同的操作。除了 .*
之外,它们都不会解引用指针。
访问类型 | 语法 | 等价的指针表达式 | |
---|---|---|---|
字段 | .字段 |
addr_of!((*ptr).field) |
|
索引 | [索引] |
ptr.cast::<T>().add(index) |
|
增加偏移量 | +计数 |
1 | ptr.add(count) |
减少偏移量 | -计数 |
1 | ptr.sub(count) |
字节增加偏移量 | u8+字节 |
1 | ptr.byte_add(bytes) |
字节减少偏移量 | u8-字节 |
1 | ptr.byte_sub(bytes) |
转换 | asT=> |
2 | ptr.cast::<T>() |
解引用 | .* |
3 | ptr.read() |
分组 | ( ... ) |
仅为了清晰起见,对内部访问进行分组。 |
-
count
/bytes
可以是整数文字或括号内包裹的表达式。 - 如果转换是组中的最后一个访问,则可以省略
=>
。 - 只有当解引用是宏的最后一个访问时,它才可能返回一个非指针值。
请注意,因为这个函数在指针上调用read()
,它很容易导致重复值。通常,仅对内部指针类型使用此访问。
依赖关系
~1.4–2MB
~41K SLoC