将指针投入结构的第一个指向结构指针的指针是合法的生锈吗?
在C中,可以将指向结构的指针投放给指向其第一个成员的指针,反之亦然。也就是说,结构的地址被定义为其第一个成员的地址。
struct Base { int x; };
struct Derived { struct Base base; int y; };
int main() {
struct Derived d = { {5}, 10 };
struct Base *base = &d.base; // OK
printf("%d\n", base->x);
struct Derived *derived = (struct Derived *)base; // OK
printf("%d\n", derived->y);
}
这通常用于实现C ++样式继承。
如果结构是repr(c)
(以使其字段不重组),Rust是否允许使用同一件事?
#[derive(Debug)]
#[repr(C)]
struct Base {
x: usize,
}
#[derive(Debug)]
#[repr(C)]
struct Derived {
base: Base,
y: usize,
}
// safety: `base` should be a reference to `Derived::base`, otherwise this is UB
unsafe fn get_derived_from_base(base: &Base) -> &Derived {
let ptr = base as *const Base as *const Derived;
&*ptr
}
fn main() {
let d = Derived {
base: Base {
x: 5
},
y: 10,
};
let base = &d.base;
println!("{:?}", base);
let derived = unsafe { get_derived_from_base(base) }; // defined behaviour?
println!("{:?}", derived);
}
该代码有效,但是它会始终有效吗?是否定义行为?
In C, a pointer to a struct can be cast to a pointer to its first member, and vice-versa. That is, the address of a struct is defined to be the address of its first member.
struct Base { int x; };
struct Derived { struct Base base; int y; };
int main() {
struct Derived d = { {5}, 10 };
struct Base *base = &d.base; // OK
printf("%d\n", base->x);
struct Derived *derived = (struct Derived *)base; // OK
printf("%d\n", derived->y);
}
This is commonly used to implement C++-style inheritance.
Is the same thing allowed in Rust if the structs are repr(C)
(so that their fields aren't reorganized)?
#[derive(Debug)]
#[repr(C)]
struct Base {
x: usize,
}
#[derive(Debug)]
#[repr(C)]
struct Derived {
base: Base,
y: usize,
}
// safety: `base` should be a reference to `Derived::base`, otherwise this is UB
unsafe fn get_derived_from_base(base: &Base) -> &Derived {
let ptr = base as *const Base as *const Derived;
&*ptr
}
fn main() {
let d = Derived {
base: Base {
x: 5
},
y: 10,
};
let base = &d.base;
println!("{:?}", base);
let derived = unsafe { get_derived_from_base(base) }; // defined behaviour?
println!("{:?}", derived);
}
The code works, but will it always work, and is it defined behaviour?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您写的方式,目前不是;但是有可能使它起作用。
引用
t
仅允许访问t
- 不再(它具有t
的出处)。 expression& d.base
为您提供了仅适用于基本
的参考。使用它访问派生
的字段是未定义的行为。目前尚不清楚这是我们想要的,有积极的讨论(也 this ),但这就是当前行为。有一个名为 miri 的好工具 可以检查锈蚀代码是否存在某些(并非全部!)未定义的行为(您可以在操场上运行它;工具 - > miri),实际上它标记您的代码:您可以通过创建对整个 derived 的参考来使其正常运行到
基础
。 The raw pointer will keep the provenance of the original reference, and thus 将有效:注:参考根本不应在此过程中 。如果您将
base
作为类型base
的引用,则将再次失去出处。这将在操场上通过Miri,但根据当前规则仍然是未定义的行为,并且会以更严格的标志失败Miri(将环境变量设置miriflags
> code>> zmiri-tag-raw-pointers < /代码>在本地运行Miri之前)。
The way you wrote it, currently not; but it is possible to make it work.
Reference to
T
is only allowed to accessT
- no more (it has provenance forT
). The expression&d.base
gives you a reference that is only valid forBase
. Using it to accessDerived
's fields is undefined behavior. It is not clear this is what we want, and there is active discussion about that (also this), but that is the current behavior. There is a good tool named Miri that allows you to check your Rust code for the presence of some (not all!) undefined behavior (you can run it in the playground; Tools->Miri), and indeed it flags your code:You can make it work by creating a reference to the whole
Derived
and casting it to a raw pointer toBase
. The raw pointer will keep the provenance of the original reference, and thus that will work:Note: References should not be involved at all in the process. If you'll reborrow
base
as a reference of typeBase
, you will lose the provenance again. This will pass Miri on the playground, but is still undefined behavior per the current rules and will fail Miri with stricter flags (set the environment variableMIRIFLAGS
to-Zmiri-tag-raw-pointers
before running Miri locally).