不安全指针损坏
Unsafe pointer becoming corrupted
我试图更好地理解 Rust 中的不安全指针。在下面的代码中,我创建了一棵树,其中每个节点都指向其父节点。当我创建一个叶子并将其作为子节点添加到节点时,一切看起来都很好。一旦我获取该节点并将其添加到另一个节点,叶子的父指针就会损坏。为什么叶子的父指针损坏了,而中间节点的父指针却完好无损?哪些更改会使它起作用?
#[derive(Clone)]
pub struct Shape<'a> {
pub id: usize,
pub parent: Option<*mut Group<'a>>,
}
type Node<'a> = Box<Group<'a>>;
#[derive(Clone)]
pub struct Group<'a> {
pub shape: Shape<'a>,
pub children: Vec<Node<'a>>,
}
pub struct GroupBuilder<'a> {
id: usize,
children: Vec<Node<'a>>,
}
impl<'a> GroupBuilder<'a> {
pub fn new(id: usize) -> GroupBuilder<'a> {
GroupBuilder {
id,
children: Vec::new(),
}
}
pub fn build(&self) -> Node<'a> {
unsafe {
let group: *mut Group = Box::into_raw(Box::new(Group {
shape: Shape {
id: self.id,
parent: Option::None,
},
children: self.children.clone(),
}));
for child in (*group).children.iter_mut() {
child.shape.parent = Option::Some(group.clone());
}
Box::from_raw(group)
}
}
pub fn add_child(&mut self, child: Node<'a>) -> &mut GroupBuilder<'a> {
self.children.push(child);
self
}
}
pub fn main() {
unsafe {
// THIS WORKS!
let leaf = GroupBuilder::new(1).build();
assert_eq!(leaf.shape.id, 1);
assert_eq!(leaf.shape.parent, Option::None);
// THIS WORKS AS WELL!
let node = GroupBuilder::new(2).add_child(leaf).build();
assert_eq!(node.shape.id, 2);
assert_eq!(node.children[0].shape.id, 1);
assert_eq!((*node.children[0].shape.parent.unwrap()).shape.id, 2);
// THIS WORKS FOR ROOT -> NODE, BUT BREAKS ON ROOT -> NODE -> X
let root = GroupBuilder::new(3).add_child(node).build();
assert_eq!(root.shape.id, 3);
assert_eq!(root.shape.parent, Option::None);
assert_eq!(root.children[0].shape.id, 2);
assert_eq!((*root.children[0].shape.parent.unwrap()).shape.id, 3);
assert_eq!(root.children[0].children[0].shape.id, 1);
// THINGS BREAK HERE!
assert_eq!(
(*root.children[0].children[0].shape.parent.unwrap())
.shape
.id,
2
);
}
}
我收到以下错误:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 1.54s
Running `target/debug/playground`
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `93906121591344`,
right: `2`', src/main.rs:77:9
问题出在 build
中的 self.children.clone()
。让我们跟随 step-by-step 创建 root
:
时会发生什么
- 已创建临时
GroupBuilder
(我们将其命名为 tmp
)。
node
移入 tmp.children
.
tmp.children
被克隆,克隆被放入root.children
。由于 node
是 Box
,它也被克隆了。
- 是临时的,
tmp
在语句末尾被删除,所以 tmp.children
被删除,所以 node
被释放,leaf
中的指针变得悬空。
通过允许 build
获得 GroupBuilder
的所有权,我们可以使最终断言成功 - playground(注意我还更改了 add_child
的签名从 &mut Self -> &mut Self
到 Self -> Self
,因此链接仍然可用。
但是请注意,此代码示例仍被 Miri 视为 UB - 当有指针指向 Box
的内部时,它可能认为您无法移动 Box
.这可能是 Miri 的限制,但也有可能你确实做了一些不合理的事情。
我试图更好地理解 Rust 中的不安全指针。在下面的代码中,我创建了一棵树,其中每个节点都指向其父节点。当我创建一个叶子并将其作为子节点添加到节点时,一切看起来都很好。一旦我获取该节点并将其添加到另一个节点,叶子的父指针就会损坏。为什么叶子的父指针损坏了,而中间节点的父指针却完好无损?哪些更改会使它起作用?
#[derive(Clone)]
pub struct Shape<'a> {
pub id: usize,
pub parent: Option<*mut Group<'a>>,
}
type Node<'a> = Box<Group<'a>>;
#[derive(Clone)]
pub struct Group<'a> {
pub shape: Shape<'a>,
pub children: Vec<Node<'a>>,
}
pub struct GroupBuilder<'a> {
id: usize,
children: Vec<Node<'a>>,
}
impl<'a> GroupBuilder<'a> {
pub fn new(id: usize) -> GroupBuilder<'a> {
GroupBuilder {
id,
children: Vec::new(),
}
}
pub fn build(&self) -> Node<'a> {
unsafe {
let group: *mut Group = Box::into_raw(Box::new(Group {
shape: Shape {
id: self.id,
parent: Option::None,
},
children: self.children.clone(),
}));
for child in (*group).children.iter_mut() {
child.shape.parent = Option::Some(group.clone());
}
Box::from_raw(group)
}
}
pub fn add_child(&mut self, child: Node<'a>) -> &mut GroupBuilder<'a> {
self.children.push(child);
self
}
}
pub fn main() {
unsafe {
// THIS WORKS!
let leaf = GroupBuilder::new(1).build();
assert_eq!(leaf.shape.id, 1);
assert_eq!(leaf.shape.parent, Option::None);
// THIS WORKS AS WELL!
let node = GroupBuilder::new(2).add_child(leaf).build();
assert_eq!(node.shape.id, 2);
assert_eq!(node.children[0].shape.id, 1);
assert_eq!((*node.children[0].shape.parent.unwrap()).shape.id, 2);
// THIS WORKS FOR ROOT -> NODE, BUT BREAKS ON ROOT -> NODE -> X
let root = GroupBuilder::new(3).add_child(node).build();
assert_eq!(root.shape.id, 3);
assert_eq!(root.shape.parent, Option::None);
assert_eq!(root.children[0].shape.id, 2);
assert_eq!((*root.children[0].shape.parent.unwrap()).shape.id, 3);
assert_eq!(root.children[0].children[0].shape.id, 1);
// THINGS BREAK HERE!
assert_eq!(
(*root.children[0].children[0].shape.parent.unwrap())
.shape
.id,
2
);
}
}
我收到以下错误:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 1.54s
Running `target/debug/playground`
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `93906121591344`,
right: `2`', src/main.rs:77:9
问题出在 build
中的 self.children.clone()
。让我们跟随 step-by-step 创建 root
:
- 已创建临时
GroupBuilder
(我们将其命名为tmp
)。 node
移入tmp.children
.tmp.children
被克隆,克隆被放入root.children
。由于node
是Box
,它也被克隆了。- 是临时的,
tmp
在语句末尾被删除,所以tmp.children
被删除,所以node
被释放,leaf
中的指针变得悬空。
通过允许 build
获得 GroupBuilder
的所有权,我们可以使最终断言成功 - playground(注意我还更改了 add_child
的签名从 &mut Self -> &mut Self
到 Self -> Self
,因此链接仍然可用。
但是请注意,此代码示例仍被 Miri 视为 UB - 当有指针指向 Box
的内部时,它可能认为您无法移动 Box
.这可能是 Miri 的限制,但也有可能你确实做了一些不合理的事情。