< 返回版块

TheLudlows 发表于 2021-04-28 17:51

Tags:PhantomData

Playground 链接

请问链接中的代码如何修复

评论区

写评论
modraedlau 2021-04-30 17:01

这是一个故意人为造成 drop check 的问题,详情请查看文档:[Drop Check]https://doc.rust-lang.org/nomicon/dropck.html。

在main中,故意把MyBox的绑定的_b提前到x之前,这样照成的结果是Foo(1)会先于MyBox Drop:

let _b;
let x = Foo(1);
_b = MyBox::new(&x);

rust安全最主要care的问题是check代码不要出现悬垂指针。如果MyBox没有人为加了impl Drop的实现,或者我们直接先注释掉MyBoximpl Drop实现:

// impl<T> Drop for MyBox<T> {
//     fn drop(&mut self) {
//         unsafe {
//             Box::from_raw(self.raw);
//         }
//         println!("drop MyBox");
//     }
// }

那么代码就能通过编译,因为此时其实Foo(1)MyBox几乎同时Drop,谁先谁后其实无所谓对于编译器来说,因为不会出现悬垂指针。但是你一但为MyBox加入了impl Drop,这个时候编译器就会怀疑你的impl Drop可能有对Foo(1)的引用,如果Foo(1)提前Drop,那么就会出现悬垂指针,所以编译器一定会让你保证Foo(1)要后于MyBox Drop。 假如在impl Drop确实没有对Foo(1)的引用,我们要让编译通过可以使用may_dangle(文档中有介绍)来逃逸掉这样的分析,相当于告诉编译器:我的Drop实现是安全的,没有对Foo(1)的引用,不要帮我在这帮我进行Drop Check。 但实际上我们可以看到代码中MyBoximpl DropFoo(1)的指针(unsafe代码)。所以就算我们让编译器不进行Drop Check,这里也有很大的风险。

解决此问题的方式有很多,关键看你的目的是什么,比如我们让Foo(1)MyBox之后Drop:

let x = Foo(1);
let _b = MyBox::new(&x);

另一种是不要去显示实现MyBoxDrop trait(前面有解释原因)。

还有一种是不要在impl Drop有任何Foo(1)的指针,然后用may_dangle进行逃逸掉check(注意这个只能使用nightly version)。

xjkdev 2021-04-30 00:56

你确定你想的是 _b = MyBox::new(&x); 中使用 &x 吗?这意味着将创建一个 MyBox<&Foo> 这种情况下我觉得编译器报错没有问题,因为这确实是一个悬浮指针的可能情况。二楼的方法似乎只是手动关闭了这个检查,但可能没有解决潜在的问题。

如果你要创建的是 MyBox<Foo> ,应该直接用 MyBox::new(x)MyBox::new(x.clone()) (如果Foo实现了Clone)。

Bai-Jinlin 2021-04-28 19:48

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=bba6c64cd3da62db75abbf096a3d766b

10ty2long 2021-04-28 19:00

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9b4cf9ffd23e2fec057619a59df8048e

1 共 4 条评论, 1 页