一、Pin由来
在Rust中,自引用结构会导致,此变量被move后,其内部自引用的指针指向不改变,从而存在安全隐患 。
注意:Pin是一个struct。UnPin和!UnPin是trait。这个要分清。
二、方案
对自引用结构,如何增加安全性,方案以下两步:
1、里面加phantompinned,
2、外面套上Pin,
这样新的结构被move后,可以保证里面的自引用的指针指向的内容正确。
基于以上分析,我们来进行相关验证
1、验证普通的结构体move前后行为
2、带自引用结构move前后的行为
3、带PhantomPinned自引用结构move前后的行为
在行为方面,主要考察,指针地址,指针指向内容的变化,是否如上所述。
三、相关代码
use std::pin::{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后,对应指针可以正确指向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,}}fn set_ref_value(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;}
}
fn main() {let _free = Free::default();let mut _self_ref = SelfRef::default();_self_ref.set_ref_value();let mut self_ref_pin = SelfRefPinned::default();let mut _self_ref_pinned = unsafe { Pin::new_unchecked(&mut self_ref_pin) };SelfRefPinned::set_ref_value(_self_ref_pinned.as_mut());// before moveprintln!("-----------before move-------------");println!("_free content:{:?} content 内存指针: {:p}",_free.content,&_free.content);// after moveprintln!("-----------after move-------------");let free_ = _free; //move操作println!("free_ content:{:?} content 内存指针: {:p}",free_.content,&free_.content);println!("-----------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);// after moveprintln!("-----------after move-------------");let self_ref_ = _self_ref; //move操作println!("self_ref_ 内存地址:{:p} content内存地址:{:p}",&self_ref_,&self_ref_.content);println!("self_ref_._ptr对应value :{:?} 自引用指针的内存地址: {:?}",unsafe{&*self_ref_._ptr},&self_ref_._ptr);println!("-----------before move-------------");println!("_self_ref_pinned 内存地址:{:p} content内存地址:{:p}",&_self_ref_pinned,&_self_ref_pinned.content);println!("_self_ref_pinned._ptr对应value :{:?} 自引用指针的内存地址: {:?}",unsafe{&*_self_ref_pinned._ptr},&_self_ref_pinned._ptr);// after moveprintln!("-----------after move-------------");let self_ref_pinned_ = _self_ref_pinned; //move 操作println!("self_ref_pinned_ 内存地址:{:p} content内存地址:{:p}",&self_ref_pinned_,&self_ref_pinned_.content);println!("self_ref_pinned_._ptr对应value :{:?} 自引用指针的内存地址: {:?}",unsafe{&*self_ref_pinned_._ptr},&self_ref_pinned_._ptr);}
四、输出
-----------before move-------------
_free content:"" content 内存指针: 0x7ffc25c25850
-----------after move-------------
free_ content:"" content 内存指针: 0x7ffc25c25830
-----------before move-------------
_self_ref 内存地址:0x7ffc25c257f0 content内存地址:0x7ffc25c257f0
_self_ref._ptr对应value :"hello world!" 自引用指针的内存地址: 0x7ffc25c257f0
-----------after move-------------
self_ref_ 内存地址:0x7ffc25c257c0 content内存地址:0x7ffc25c257c0
self_ref_._ptr对应value :"hello world!" 自引用指针的内存地址: 0x7ffc25c257f0
-----------before move-------------
_self_ref_pinned 内存地址:0x7ffc25c257b8 content内存地址:0x7ffc25c25810
_self_ref_pinned._ptr对应value :"hello world!" 自引用指针的内存地址: 0x7ffc25c25810
-----------after move-------------
self_ref_pinned_ 内存地址:0x7ffc25c257e8 content内存地址:0x7ffc25c25810
self_ref_pinned_._ptr对应value :"hello world!" 自引用指针的内存地址: 0x7ffc25c25810
五、总结
1、从上面Free类型来看,move后,struct主体的指针有变动。
2、自引用结构变动后,可以看见对应的struct指针地址发生变化后,自引用对应指针并没有变动,并没指向所需的content的内容。
3、Pin之后,move其实没有任何影响。