一、Pin由来
在Rust中,自引用结构会导致,此变量被move后,其内部自引用的指针指向不改变,从而存在安全隐患 。
注意:Pin是一个struct。UnPin和!UnPin是trait。这个要分清。
二、方案
对原始的自引用结构,如何增加安全性,方案有以下两步:
1、里面加Phantompinned,
2、外面套上Pin,
这样新的结构被move后,可以保证里面的自引用的指针指向的内容正确。
基于以上分析,我们来进行相关验证:
1、验证普通的结构体move前后行为:move的行为会产生什么影响?
2、带自引用结构move前后的行为:是否存在安全隐患?
3、带PhantomPinned自引用结构move前后的行为:是否解决了原先产生的问题?
三、相关代码
因为不涉及外部库,cargo.toml文件不需要另列。main.rs代码如下:
use std::pin::Pin;
use std::marker::PhantomPinned;
// unpin结构,没有自引用
#[derive(Debug,Default)]
struct Free{
content: String,
}
// unpin结构 =>move后,ptr对应的指针还会指向原来的地址,但不能正确指向content内容
#[derive(Debug)]
struct SelfRef {
content: String,
_ptr: *const String,//对应content 或者是&String
}
impl SelfRef{
fn default() ->Self{
SelfRef{
content: String::from("hello world!"),
_ptr: std::ptr::null(),
}
}
fn set_ref_value(&mut self){
self._ptr = & self.content as *const String;
}
}
// !unpin结构,通过引入phantompinned,再pin后,SelfRefPinned被move后,_ptr对应指针可以正确指向content
#[derive(Debug)]
struct SelfRefPinned {
content: String,
_ptr: *const String,//对应content
_marker: PhantomPinned,
}
impl SelfRefPinned{
fn default() ->Self{
SelfRefPinned{
content: String::from("hello world!"),
_ptr: std::ptr::null(),
_marker: PhantomPinned,
}
}
// stack
fn set_ref_value_by_ref_mut(pinned_obj: Pin<&mut Self>) {
let content_ptr = &pinned_obj.content as *const String;
let mut_self_ref: &mut SelfRefPinned = unsafe { pinned_obj.get_unchecked_mut() };
mut_self_ref._ptr = content_ptr;
}
// heap
fn set_ref_value_by_box_pin(mut pinned_obj: Pin<Box<Self>>) ->Pin<Box<Self>>{
let content_ptr = &pinned_obj.content as *const String;
unsafe {
pinned_obj.as_mut().get_unchecked_mut()._ptr = content_ptr
};
pinned_obj
}
}
fn detect_free_and_self_ref(){
println!("\n----Free、SelfRef两个unpin对象move前后分析------------");
let free = Free::default();
let mut self_ref = SelfRef::default();
self_ref.set_ref_value();
println!("\n-----------before move-------------");
println!("free content:{:?} content 内存指针:{:p}",free.content,&free.content);
// Free after move
println!("-----------after move-------------");
let free_move = free; //第1次move操作
println!("free_move content:{:?} content 内存指针:{:p}",free_move.content,&free_move.content);
// SelfRef before move
println!("\n-----------before move-------------");
println!("self_ref 内存地址:{:p} content 内存地址:{:p}",&self_ref,&self_ref.content);
println!("self_ref._ptr 对应value:{:?} 自引用指针的内存地址:{:?}",unsafe{&*self_ref._ptr},self_ref._ptr);
// SelfRef after move
println!("-----------after move-------------");
let self_ref_move = self_ref; //第1次move操作
println!("self_ref_move 内存地址:{:p} content 内存地址:{:p}",&self_ref_move,&self_ref_move.content);
println!("self_ref_move._ptr 对应value:{:?} 自引用指针的内存地址:{:?}",unsafe{&*self_ref_move._ptr},self_ref_move._ptr);
// SelfRef move again
println!("-----------after move again -------------");
let self_ref_move2 = self_ref_move; //第2次 move操作
println!("self_ref_move2 内存地址:{:p} content 内存地址:{:p}",&self_ref_move2,&self_ref_move2.content);
println!("self_ref_move2._ptr 对应value:{:?} 自引用指针的内存地址:{:?}",unsafe{&*self_ref_move2._ptr},self_ref_move2._ptr);
}
fn detect_self_ref_pin_by_ref_mut(){
let mut obj = SelfRefPinned::default();
// obj不执行shadow
let pinned_obj = unsafe { Pin::new_unchecked( &mut obj) };
// obj => 执行shadow
// let obj =unsafe { Pin::new_unchecked( &mut obj) }; //
// 经过shadow后,obj才算Pin住!
SelfRefPinned::set_ref_value_by_ref_mut(pinned_obj);
println!("\n----Pin在stack上: !unpin trait结构体对象move前后分析------------");
println!("\n-----------before move-------------");
println!("obj 内存地址:{:p} content 内存地址:{:p}",&obj,&obj.content);
println!("obj._ptr 对应value:{:?} 自引用指针的内存地址:{:?}",unsafe{&*obj._ptr},obj._ptr);
println!("-----------after move-------------");
// self_ref_pin 结构体 move
let obj_move = obj;
println!("obj_move 内存地址:{:p} content 内存地址:{:p}",&obj_move,&obj_move.content);
println!("obj_move._ptr 对应value:{:?} 自引用指针的内存地址:{:?}",unsafe{&*obj_move._ptr},obj_move._ptr);
println!("\n");
}
fn detect_sef_ref_pin_by_pin_box(){
let obj = SelfRefPinned::default();// obj已经被move掉了,已经不再能move了
let pinned_obj = Box::pin(obj);
let pinned_obj = SelfRefPinned::set_ref_value_by_box_pin(pinned_obj);
println!("\n----Pin在堆上: Pin<Box<T> where T:!unpin对象move前后分析------------");
// Pin<&mut SelfRefPinned> before move
println!("\n-----------before move-------------");
println!("pinned_obj 内存地址:{:p} content 内存地址:{:p}",&pinned_obj,&pinned_obj.content);
println!("pinned_obj._ptr 对应value:{:?} 自引用指针的内存地址:{:?}",unsafe{&*pinned_obj._ptr},pinned_obj._ptr);
//Pin<&mut SelfRefPinned> after move
println!("-----------after move-------------");
let pinned_obj_move = pinned_obj; //move 操作
println!("pinned_obj_move 内存地址:{:p} content 内存地址:{:p}",&pinned_obj_move,&pinned_obj_move.content);
println!("pinned_obj_move._ptr 对应value:{:?} 自引用指针的内存地址:{:?}",unsafe{&*pinned_obj_move._ptr},pinned_obj_move._ptr);
//drop(pinned_obj_move);
println!("\n");
}
fn main() {
detect_free_and_self_ref();
detect_self_ref_pin_by_ref_mut();
detect_sef_ref_pin_by_pin_box();
}
四、输出
----Free、SelfRef两个unpin对象move前后分析------------
-----------before move-------------
free content:"" content 内存指针:0x7ffd71b98f28
-----------after move-------------
free_move content:"" content 内存指针:0x7ffd71b98f00
-----------before move-------------
self_ref 内存地址:0x7ffd71b98ee0 content 内存地址:0x7ffd71b98ee0
self_ref._ptr 对应value:"hello world!" 自引用指针的内存地址:0x7ffd71b98ee0
-----------after move-------------
self_ref_move 内存地址:0x7ffd71b98ec0 content 内存地址:0x7ffd71b98ec0
self_ref_move._ptr 对应value:"hello world!" 自引用指针的内存地址:0x7ffd71b98ee0
-----------after move again -------------
self_ref_move2 内存地址:0x7ffd71b98ea0 content 内存地址:0x7ffd71b98ea0
self_ref_move2._ptr 对应value:"hello world!" 自引用指针的内存地址:0x7ffd71b98ee0
----Pin在stack上: !unpin trait结构体对象move前后分析------------
-----------before move-------------
obj 内存地址:0x7ffd71b98ec0 content 内存地址:0x7ffd71b98ec0
obj._ptr 对应value:"hello world!" 自引用指针的内存地址:0x7ffd71b98ec0
-----------after move-------------
obj_move 内存地址:0x7ffd71b98ea0 content 内存地址:0x7ffd71b98ea0
obj_move._ptr 对应value:"hello world!" 自引用指针的内存地址:0x7ffd71b98ec0
----Pin在堆上: Pin<Box<T> where T:!unpin对象move前后分析------------
-----------before move-------------
pinned_obj 内存地址:0x7ffd71b98f00 content 内存地址:0x55e54a4f5a10
pinned_obj._ptr 对应value:"hello world!" 自引用指针的内存地址:0x55e54a4f5a10
-----------after move-------------
pinned_obj_move 内存地址:0x7ffd71b98ee0 content 内存地址:0x55e54a4f5a10
pinned_obj_move._ptr 对应value:"hello world!" 自引用指针的内存地址:0x55e54a4f5a10
五、总结
1、几点观察
2、相关问题
正因为此,异步的Future才需要通过Pin来堵住这个安全的漏洞。这个才是Pin的价值所在。
3、Pin的效果
需要说明的是,如果对象是unpin的,再如何Pin也是pin不住的。
关于Pin的效果,不知道是否可以打个比喻来加深理解:
象一个学生上教育培训中课外班。如果碰到天资尚可(PhantomPinned)且愿意学(!unpin)的学生,课外班(Pin)是有效的;如果是天资愚笨的(没有PhantomPinned)或不愿意学(unpin)的,课外班是无效的,花钱起不到效果。