TheLudlows 发表于 2021-04-28 17:51
Tags:PhantomData
Playground 链接
请问链接中的代码如何修复
这是一个故意人为造成 drop check 的问题,详情请查看文档:[Drop Check]https://doc.rust-lang.org/nomicon/dropck.html。
在main中,故意把MyBox的绑定的_b提前到x之前,这样照成的结果是Foo(1)会先于MyBox Drop:
MyBox
_b
x
Foo(1)
let _b; let x = Foo(1); _b = MyBox::new(&x);
rust安全最主要care的问题是check代码不要出现悬垂指针。如果MyBox没有人为加了impl Drop的实现,或者我们直接先注释掉MyBox的impl Drop实现:
impl 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。 但实际上我们可以看到代码中MyBox的impl Drop有Foo(1)的指针(unsafe代码)。所以就算我们让编译器不进行Drop Check,这里也有很大的风险。
may_dangle
解决此问题的方式有很多,关键看你的目的是什么,比如我们让Foo(1)在MyBox之后Drop:
let x = Foo(1); let _b = MyBox::new(&x);
另一种是不要去显示实现MyBox的Drop trait(前面有解释原因)。
Drop
还有一种是不要在impl Drop有任何Foo(1)的指针,然后用may_dangle进行逃逸掉check(注意这个只能使用nightly version)。
你确定你想的是 _b = MyBox::new(&x); 中使用 &x 吗?这意味着将创建一个 MyBox<&Foo> 这种情况下我觉得编译器报错没有问题,因为这确实是一个悬浮指针的可能情况。二楼的方法似乎只是手动关闭了这个检查,但可能没有解决潜在的问题。
_b = MyBox::new(&x);
&x
MyBox<&Foo>
如果你要创建的是 MyBox<Foo> ,应该直接用 MyBox::new(x) 或 MyBox::new(x.clone()) (如果Foo实现了Clone)。
MyBox<Foo>
MyBox::new(x)
MyBox::new(x.clone())
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=bba6c64cd3da62db75abbf096a3d766b
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9b4cf9ffd23e2fec057619a59df8048e
评论区
写评论这是一个故意人为造成 drop check 的问题,详情请查看文档:[Drop Check]https://doc.rust-lang.org/nomicon/dropck.html。
在main中,故意把
MyBox
的绑定的_b
提前到x
之前,这样照成的结果是Foo(1)
会先于MyBox
Drop:rust安全最主要care的问题是check代码不要出现悬垂指针。如果
MyBox
没有人为加了impl Drop
的实现,或者我们直接先注释掉MyBox
的impl Drop
实现:那么代码就能通过编译,因为此时其实
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。 但实际上我们可以看到代码中MyBox
的impl Drop
有Foo(1)
的指针(unsafe代码)。所以就算我们让编译器不进行Drop Check,这里也有很大的风险。解决此问题的方式有很多,关键看你的目的是什么,比如我们让
Foo(1)
在MyBox
之后Drop:另一种是不要去显示实现
MyBox
的Drop
trait(前面有解释原因)。还有一种是不要在
impl Drop
有任何Foo(1)
的指针,然后用may_dangle
进行逃逸掉check(注意这个只能使用nightly version)。你确定你想的是
_b = MyBox::new(&x);
中使用&x
吗?这意味着将创建一个MyBox<&Foo>
这种情况下我觉得编译器报错没有问题,因为这确实是一个悬浮指针的可能情况。二楼的方法似乎只是手动关闭了这个检查,但可能没有解决潜在的问题。如果你要创建的是
MyBox<Foo>
,应该直接用MyBox::new(x)
或MyBox::new(x.clone())
(如果Foo实现了Clone)。https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=bba6c64cd3da62db75abbf096a3d766b
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9b4cf9ffd23e2fec057619a59df8048e