我正在使用动态大小类型的FFI库编写安全的包装,并且我试图确定在Rust中处理它们的最佳方法。
特别是,我包装了Windows API的SID
结构。它有一个BYTE(u8
)指示当前版本,一个BYTE(u8
)指示子授权机构的数量,六个BYTE([u8; 6]
)指示标识符授权,然后有一个一系列DWORD(u32
),代表子机构的数量。它具有的子机构数与子机构数相同。
通常,方法是只通过WinAPI调用来处理SID,所以我应该永远只有一个指向它的指针。此外,通过将内部部件保密,我可以确保我的板条箱的消费者只能通过我控制的引用Box
等访问它。但是,我确实需要声明某种Sid
类型的 类型,并且我正在尝试确定最佳方法。
选项1:winapi
的板条箱只是假设只会有a single sub-authority:
pub struct SID {
Revision: BYTE,
SubAuthorityCount: BYTE,
IdentifierAuthority: SID_IDENTIFIER_AUTHORITY, // [u8; 6]
SubAuthority: [DWORD; 1],
}
仅在引用时有效,但是如果创建了拥有的Sid
并将SubAuthorityCount
设置为1以外的任何值,则WinAPI调用将读取无效的SubAuthority
条目
选项2:乐观地分配DWORD
的最大数量:
pub struct SID {
Revision: BYTE,
SubAuthorityCount: BYTE,
IdentifierAuthority: SID_IDENTIFIER_AUTHORITY, // [u8; 6]
SubAuthority: [DWORD; 256],
}
这会引入内存开销(常见的情况是有两个子权限)。这也意味着不能将WinAPI分配的SID强制转换为SID
类型,因为Rust期望存在的附加SubAuthorities
不是由SID分配的。
选项3:动态大小的数组:
pub struct SID {
Revision: BYTE,
SubAuthorityCount: BYTE,
IdentifierAuthority: SID_IDENTIFIER_AUTHORITY, // [u8; 6]
SubAuthority: [DWORD],
}
现在不再是Sized
,它可以准确反映基础内存的形状。另外,由于Sized
类型不能直接声明(我认为吗?),因此只能使用FFI调用来创建它。但是,现在所有指向它的指针都将自动变为胖指针,这使得与FFI调用进行互操作变得更加困难,而FFI调用都希望使用裸(细)指针。
选项4:永远不会分配它,所以……什么也没有。它将仅作为参考来处理,并且我将永远不会研究基础字段,因此完全没有理由公开任何内容:
pub struct SID {}
这感觉超级奇怪。我知道零大小类型在Rust中的行为与在C中不同,并且在FFI调用周围使用它们感到不舒服。但是,它的确反映了SID
类型应该使用的方式:它只能通过引用(或Box
或其他指针)使用,并且这些字段无关。但是,例如在将ZST &
强制转换为原始指针然后返回时,我绝对不确定行为。
或者还有别的吗?目前,我正在使用选项1,将所有内容设为私有,只是不直接访问任何字段(所有内容都通过WinAPI调用),但我对人们推荐的方法以及我的分析是否正确感到好奇。