繁体   English   中英

可以安全地将没有repr(C)的struct传递给C API吗?

[英]Safe to pass struct without repr(C) to C API?

根据Shepmaster的评论建议提出新问题

假设一个库定义了一个没有#[repr(C)] 如果需要将结构传递给C API,有没有一种安全的方法呢? 我是否应该担心Rust编译器会以C API不期望的方式更改结构的内存布局?

我见过一些图书馆这样做。 根据我的经验, 他们在将结构传递给C函数时使用mem::transmute transmute是否会以某种方式消除#[repr(C)]

如果我是定义结构的人,我不会问。 我只想添加#[repr(C)] 问题是我想使用我无法控制的库中的结构。

老问题

我一直在使用cgmath-rs ,如果cgmath 被放弃 ,我可能会切换到nalgebra

在这两个库的源代码中,我没有在向量和矩阵结构上看到#[repr(C)] 那么这些库如何与OpenGL API兼容? 当您将指针传递给向量和矩阵时,OpenGL需要一定的内存布局。 我的理解是,没有#[repr(C)] ,Rust结构的内存布局是未定义的。

至少在一个例子中 ,我看到mem::transmute在将它们交给OpenGL之前应用于这些结构。 我可能会误解transmute ,但该函数似乎保留了struct的内存布局。 所以,如果布局是错误的,首先,它仍然是错误的之后transmute ,是否正确?

我也考虑过vecmath 但看起来,根据设计,vecmath缺乏辅助函数来生成旋转矩阵等。 没错,我可以实现这些,但不必这样做会很好。 无论如何,vecmath的设计是否通过使用数组而不是结构来避免内存布局的问题?

我见过一些图书馆这样做。 根据我的经验,他们在将结构传递给C函数时使用mem :: transmute。 转化是否会以某种方式消除#[repr(C)]的需要?

没有。

......但它很复杂。

如果你有一个&Foo,并且你将它传递给C并且根本不编辑它 ,那么将你的&Foo转换为* const c_void并将其传递给ac调用是完全有效的,例如:

let fp = &foo as *const Foo as *const c_void

你可能会看到人们这样做是一步,使用transmute;

unsafe { ffi_call(transmute(&foo), ...) }

...但重要的是要理解转化调用不会修改内存布局; 但它确实消耗了价值

例如,此代码可能会导致以后出现段错误:

{
  let foo = Foo { ... }
  unsafe { ffi_call(&foo as _ as *const c_void); }
}

这是因为指针&foo指向foo; 但是在范围结束后foo停止存在; 因此,如果稍后使用(例如,ffi调用保持引用),将导致段错误。

你可能会认为拳击(即移动到堆)修复了这个问题:

let foo = Box::new(Foo { ... })
unsafe { ffi_call(&*foo as *const c_void); }

......但它没有; 因为当Box离开范围时,它会被丢弃。 但是,因为转化会改变价值; 此代码安全地将foo实例移动到ffi调用中以供以后使用; 请注意,如果以后不恢复该值,则会出现内存泄漏:

let foo = Box::new(Foo { ... })
unsafe { ffi_call(transmute(foo)); }

...但是没有使用transmute()可以解决repr(C)的缺失,是的,你可以期待生锈与你的结构布局混乱; 这通常与drop标志有关,并且可以作为https://github.com/rust-lang/rfcs/pull/320的一部分解决,因为drop flags目前是内存布局到结构的唯一有形的不同; 但由于RFC没有明确涵盖,我不会屏住呼吸。

即。 tldr; 如果你需要传递一个结构并在C中修改它,它需要repr(C); 如果没有,它将无法工作**。

转化被用于其他原因,完全与此无关。

** - >好吧,它可能有效,但你真正打的是未定义的行为。 如果您正在使用的库工作,可能是因为它恰好起作用。 生锈很多东西都是如此; 例如。 在某些情况下可变别名...但它确实意味着破坏了代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM