Rust: Pin 和Unpin之啰里八嗦
继续学习Future,今天就不得不研究Pin,我以为挺简单的,然而...
简单的说(总结)
- Pin:是钉住,压住的意思。放在rust中,其中Pin是一个结构体,全类型是Pin
:固定住Ptr的意思。 - Unpin是一个trait。Rust中所有的类型默认都是Unpin的。
而Pin
Pin一般的使用场景是在Future中,因为Future实际上编译后就是状态机,而Future又会引用一些变量,这些变量在Future执行时为了保证内存地址不变,所以必须pin起来,而一般不是pin某个单独的变量,是将整体pin起来。
一个在堆上自引用的例子
struct HeapSelfRef {
v: Vec<u8>,
ptr: *const u8, //v的地址
}
impl HeapSelfRef {
fn new(v: Vec<u8>) -> Self {
let ptr = v.as_ptr();
Self { v, ptr }
}
fn display(&self) {
println!("the address of &v {:p}", &self.v);
println!("the address of v current {:?}", self.v.as_ptr());
println!("the address of ptr {:?}", self.ptr);
}
}
fn test_heap() {
let heap = HeapSelfRef::new(vec![1, 2, 3]);
heap.display();
//移动heap
let heap2 = heap;
//通过打印,发现确实移动了,&v的地址(栈上)不同,但v堆上的地址没变
heap2.display();
}
上面的例子因为数据放在堆上,所以栈上的地址(堆的引用)变了,但堆上本身的地址没变。堆上的地址永远不会变?感觉也不一定
再来一个栈上自引用的例子
struct StackSelfRef {
v: u8,
ptr: *const u8, //v的地址
}
impl StackSelfRef {
fn new(v: u8) -> Self {
let mut s = Self {
v,
ptr: std::ptr::null(),
};
s.ptr = std::ptr::addr_of!(s.v);
println!("{}", unsafe { *s.ptr });
s
}
fn display(&self) {
//此时的ptr指向的已经地址已经不是v的地址了,因为在let s=StackSelfRef::new(),返回s的时候,已经发生了移动。
//建议是1.不引用栈上的地址,使用Box将数值存放到堆上。2.使用pin,将结构体在内存中的地址固定住。如下一个例子
println!("{}", unsafe { *self.ptr });
println!("the address of &v {:p}", &self.v);
println!(
"the address of v at current {:?}",
std::ptr::addr_of!(self.v)
);
println!("the address of ptr {:?}", self.ptr);
}
}
fn test_stak() {
let s = StackSelfRef::new(8);
s.display();
}
上面的例子中,在new之后,ptr就已经成了悬垂指针(空引用)了。
实现了Pin的例子
///使用pin将结构体在内存中的位置固定住不移动
///
struct StackPinedSelfRef {
v: u8,
ptr: *const u8,
// _pinned:PhantomPinned,
}
impl StackPinedSelfRef {
fn new(x: u8) -> Pin<&'static mut StackPinedSelfRef> {
let s = Self {
v: x,
ptr: std::ptr::null(),
// _pinned:PhantomPinned,
};
let s = Box::new(s);
let s = Box::leak(s);
let mut pined = Pin::static_mut(s);
pined.as_mut().ptr = std::ptr::addr_of!(pined.as_ref().v);
pined
}
fn display(&self) {
println!("{}", unsafe { *self.ptr });
println!("the address of &v {:p}", &self.v);
println!(
"the address of v at current {:?}",
std::ptr::addr_of!(self.v)
);
println!("the address of ptr {:?}", self.ptr);
}
}
fn test_stackpinned() {
let s = StackPinedSelfRef::new(10);
s.display();
let s2 = s;
s2.display();
}
写法纯属自创,根据自己对pin的理解一点点拼凑出来的,可能有不严谨的地方。
这里边有一个关键的地方:Pin
总结
Pin<T:Unpin> 表示T类型可以安全移动,Pin对这种类型的不起作用。
Pin<T:!Unpin> 表示T类型不能安全移动,Pin只对这种类型起限制作用。

浙公网安备 33010602011771号