我想知道一些例子,其中将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);
}
Run Code Online (Sandbox Code Playgroud)
该代码是可编译的,尽管它MyStruct从main到func等移动。
至于Box和Pin它们都将其内容保留在堆上,因此它似乎不受动议的影响。
因此,如果有人就这些问题详细阐述这个主题,我将不胜感激。由于其他问题和文档中没有涵盖它,因此仅仅使用Box.
Fin*_*nis 21
我认为你误会了。
PhantomPinned不会使数据不可移动。它只是说,一旦数据被固定,就永远无法再次取消固定。
因此,要使数据PhantomPinned不可移动,首先必须做到Pin这一点。
例如,如果您创建MyStruct变量的固定版本,则无法取消固定它:
fn main() {
let pinned_x = Box::pin(MyStruct::new(5));
let unpinned_x = Pin::into_inner(pinned_x);
}
Run Code Online (Sandbox Code Playgroud)
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`
Run Code Online (Sandbox Code Playgroud)
使用普通结构时,您可以毫无问题地取消固定它:
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`
Run Code Online (Sandbox Code Playgroud)
Pin和之间的区别Box它们是完全不同的概念。Pin确保它指向的数据不能被移动。Box将一些东西放在堆上。
正如您从前面的示例中看到的,两者经常结合使用,因为防止某些内容移动的最简单方法是将其放在堆上。
PhantomPin导致类成为!Unpin,这意味着一旦它们被固定,就不能再取消固定。
您可以尝试使用Pin堆栈上的值,但很快就会遇到问题。虽然它适用于不可固定的结构:
struct MyUnpinnableStruct;
fn main() {
let pinned_x = Box::pin(MyUnpinnableStruct);
let unpinned_x = Pin::into_inner(pinned_x);
}
Run Code Online (Sandbox Code Playgroud)
对于包含以下内容的结构失败PhantomPinned:
struct MyUnpinnableStruct(u32);
fn main() {
let y = MyUnpinnableStruct(7);
{
let pinned_y = Pin::new(&y);
}
// This moves y into the `drop` function
drop(y);
}
Run Code Online (Sandbox Code Playgroud)
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`
Run Code Online (Sandbox Code Playgroud)
更新: Rust1.68.0引入了std::pin::pin!()它可用于将项目固定在堆栈上。它的工作原理是获取所有权并隐藏固定的对象,使得在删除Pin.
Box没有Pin虽然 的内容Box位于堆上,因此具有恒定的地址,但您仍然可以将其从堆移回堆栈,这对于对象来说是不可能的Pin:
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);
}
Run Code Online (Sandbox Code Playgroud)
Address: 0x557452040ad0
Address: 0x7ffde8f7f0d4
Run Code Online (Sandbox Code Playgroud)
Pin要确保对象固定在成员函数中,可以使用以下语法:
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`
Run Code Online (Sandbox Code Playgroud)
与 一起PhantomPinned,您现在可以 100% 确定始终为同一对象打印相同的地址print_addr:
// 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();
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,绝对无法再次取消固定x_pinned,print_addr只能在固定对象上调用。
为什么这有用?例如,因为您现在可以按照Future特征中的要求使用原始指针。
但总的来说,Pin只有与代码配合使用才真正有用unsafe。没有unsafe代码,借用检查器足以跟踪您的对象。
| 归档时间: |
|
| 查看次数: |
6349 次 |
| 最近记录: |