我想知道将T类型保存在Box中是不安全的,而在Pin中则是安全的。
最初,我认为std::marker::PhantomPinned可以阻止实例被移动(通过禁止它),但表面上没有。因为:
use std::pin::Pin;
use std::marker::PhantomPinned;
#[derive(Debug)]
struct MyStruct {
field: u32,
_pin: PhantomPinned
}
impl MyStruct {
fn new(field: u32) -> Self {
Self {
field,
_pin: PhantomPinned,
}
}
}
fn func(x: MyStruct) {
println!("{:?}", x);
func2(x);
}
fn func2(x: MyStruct) {
println!("{:?}", x);
}
fn main() {
let x = MyStruct::new(5);
func(x);
}这段代码是可编译的,尽管它将MyStruct从main移动到func等等。
至于Box和Pin,它们都将其内容保存在堆中,因此它似乎不受运动的影响。
因此,如果有人就这些问题详细阐述这一专题,我将不胜感激。由于在其他问题和文档中没有涉及到这个问题,所以仅仅通过Box就能解决问题。
发布于 2022-06-06 12:23:33
我觉得你误会了。
PhantomPinned做而不是使数据不可移动。它只是说一旦数据被固定,它就再也不能被解除了。
因此,要使使用PhantomPinned的数据不可移动,您必须首先使用Pin。
例如,如果创建MyStruct变量的固定版本,则无法解锁它:
fn main() {
let pinned_x = Box::pin(MyStruct::new(5));
let unpinned_x = Pin::into_inner(pinned_x);
}error[E0277]: `PhantomPinned` cannot be unpinned
--> src/main.rs:20:38
|
20 | let unpinned_x = Pin::into_inner(pinned_x);
| --------------- ^^^^^^^^ within `MyStruct`, the trait `Unpin` is not implemented for `PhantomPinned`
| |
| required by a bound introduced by this call
|
= note: consider using `Box::pin`
note: required because it appears within the type `MyStruct`
--> src/main.rs:4:8
|
4 | struct MyStruct {
| ^^^^^^^^
note: required by a bound in `Pin::<P>::into_inner`
--> /home/martin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/pin.rs:482:23
|
482 | impl<P: Deref<Target: Unpin>> Pin<P> {
| ^^^^^ required by this bound in `Pin::<P>::into_inner`使用普通的结构,您可以在没有问题的情况下解开它:
struct MyUnpinnableStruct;
fn main() {
let pinned_x = Box::pin(MyUnpinnableStruct);
let unpinned_x = Pin::into_inner(pinned_x);
}Pin与Box的区别
它们都是完全不同的概念。Pin确保它所指向的数据不能移动。Box在堆中放了一些东西。
正如您从前面的示例中可以看到的那样,这两种方法通常都是结合使用的,因为防止某物移动的最简单方法是将其放到堆中。
PhantomPin使类成为!Unpin,这意味着一旦它们被固定,它们就不能再被解除固定。
您可以尝试对堆栈上的值使用Pin,但很快就会遇到问题。当它适用于可解锁的结构时:
struct MyUnpinnableStruct(u32);
fn main() {
let y = MyUnpinnableStruct(7);
{
let pinned_y = Pin::new(&y);
}
// This moves y into the `drop` function
drop(y);
}对于包含PhantomPinned的结构,它失败。
fn main() {
let x = MyStruct::new(5);
{
// This fails; pinning a reference to a stack object
// will fail, because once we drop that reference the
// object will be movable again. So we cannot `Pin` stack objects
let pinned_x = Pin::new(&x);
}
// This moves x into the `drop` function
drop(x);
}error[E0277]: `PhantomPinned` cannot be unpinned
--> src/main.rs:24:33
|
24 | let pinned_x = Pin::new(&x);
| -------- ^^ within `MyStruct`, the trait `Unpin` is not implemented for `PhantomPinned`
| |
| required by a bound introduced by this call
|
= note: consider using `Box::pin`
note: required because it appears within the type `MyStruct`
--> src/main.rs:4:8
|
4 | struct MyStruct {
| ^^^^^^^^
note: required by a bound in `Pin::<P>::new`
--> /home/martin/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/pin.rs:482:23
|
482 | impl<P: Deref<Target: Unpin>> Pin<P> {
| ^^^^^ required by this bound in `Pin::<P>::new`Box无Pin
虽然Box的内容在堆上,因此具有一个常量地址,但仍然可以将其从堆移回堆栈,这在Pin对象中是不可能的:
// Note that MyData does not implement Clone or Copy
struct MyData(u32);
impl MyData {
fn print_addr(&self) {
println!("Address: {:p}", self);
}
}
fn main() {
// On the heap
let x_heap = Box::new(MyData(42));
x_heap.print_addr();
// Moved back on the stack
let x_stack = *x_heap;
x_stack.print_addr();
}Address: 0x557452040ad0
Address: 0x7ffde8f7f0d4实施Pin
要确保将对象固定在成员函数中,可以使用以下语法:
fn print_addr(self: Pin<&Self>)与PhantomPinned一起,您现在可以100%地确定print_addr将始终为同一个对象打印相同的地址:
use std::{marker::PhantomPinned, pin::Pin};
struct MyData(u32, PhantomPinned);
impl MyData {
fn print_addr(self: Pin<&Self>) {
println!("Address: {:p}", self);
}
}
fn main() {
// On the heap
let x_pinned = Box::pin(MyData(42, PhantomPinned));
x_pinned.as_ref().print_addr();
// Moved back on the stack
let x_unpinned = Pin::into_inner(x_pinned); // FAILS!
let x_stack = *x_unpinned;
let x_pinned_again = Box::pin(x_stack);
x_pinned_again.as_ref().print_addr();
}在本例中,绝对没有办法再次解除x_pinned,而且只能在固定对象上调用print_addr。
为什么这个有用?例如,因为您现在可以使用原始指针,这在Future特性中是必需的。
但是通常,只有在与Pin代码配对时,unsafe才真正有用。如果没有unsafe代码,借用检查器就足以跟踪对象。
https://stackoverflow.com/questions/72516441
复制相似问题