Rust中Pin与非Pin函数对比:如何体现Pin的作用?
如何让Rust的Pin特性体现实际作用
你的代码看不到Pin效果的核心原因是:你使用的i32、&str都是实现了Unpin trait的类型。对于这类类型,Pin<T>不会施加任何移动限制——即使被Pin包裹,它们仍然可以像普通值一样被移动,所以带Pin和不带Pin的函数表现完全一致。
要看到Pin的实际作用,你需要使用非Unpin类型,也就是那些不能被安全移动的类型,最典型的就是自引用结构体(结构体内部持有指向自身的指针)。下面是修改后的代码示例:
use std::fmt; use std::pin::Pin; use std::ptr; use std::marker::PhantomPinned; // 定义一个自引用结构体,用PhantomPinned标记为!Unpin(不可安全移动) #[derive(Debug)] struct SelfRef { data: String, ptr_to_data: *const String, _pin: PhantomPinned, } impl SelfRef { // 创建自引用结构体的安全方法:必须在Pin<Box<Self>>中初始化 fn new(s: String) -> Pin<Box<SelfRef>> { let mut boxed = Box::new(SelfRef { data: s, ptr_to_data: ptr::null(), _pin: PhantomPinned, }); // 获取data的指针 let ptr = &boxed.data as *const String; // 因为结构体被Pin在Box里,所以可以安全地设置自引用 unsafe { Pin::as_mut(&mut boxed).get_unchecked_mut().ptr_to_data = ptr; } Pin::new(boxed) } // 尝试读取自引用指向的数据 fn read_data(self: Pin<&SelfRef>) -> &str { unsafe { &*self.ptr_to_data } } } // 不带Pin的情况:移动自引用结构体后,自引用会失效 fn without_pin(mut s: Box<SelfRef>) { let original_ptr = s.ptr_to_data; // 移动结构体(这里只是重新赋值,模拟移动) let moved = s; // 尝试读取移动后的自引用,会导致未定义行为(UB) unsafe { println!("Without Pin, moved data: {}", &*moved.ptr_to_data); println!("Original ptr now points to: {}", &*original_ptr); // 这里会访问已失效的内存 } } fn with_pin(pin: Pin<Box<SelfRef>>) { // Pin阻止了我们直接移动内部的结构体 println!("With Pin, data: {}", pin.read_data()); // 无法像普通Box一样取出内部结构体,避免了移动导致的自引用失效 // let moved = pin.into_inner(); // 编译错误,因为SelfRef是!Unpin } fn main() { // 带Pin的情况:安全使用自引用 let pin = SelfRef::new("Hello Pin!".to_string()); with_pin(pin); // 不带Pin的情况:移动后自引用失效(注意:实际运行可能崩溃或输出乱码,属于UB) let mut unsafe_box = Box::new(SelfRef { data: "Unsafe Move".to_string(), ptr_to_data: ptr::null(), _pin: PhantomPinned, }); let ptr = &unsafe_box.data as *const String; unsafe_box.ptr_to_data = ptr; without_pin(unsafe_box); }
关键说明:
PhantomPinned:这个标记trait会让结构体自动实现!Unpin,告诉Rust该类型不能被安全移动。- 自引用初始化:必须在
Pin<Box<Self>>中初始化自引用,因为Pin保证了结构体的内存位置不会改变,所以自引用的指针永远有效。 - Pin的限制:对于
!Unpin类型,Pin<Box<T>>无法通过into_inner()取出内部的Box<T>,也就阻止了移动操作,避免了自引用失效导致的悬垂指针。 - 不带Pin的危险:如果移动自引用结构体,内部的指针会指向原来的内存位置,而原来的内存已经被释放或重新分配,访问它会导致未定义行为(程序崩溃、输出乱码等)。
通过这个示例,你就能清晰看到Pin的作用:固定住非Unpin类型的内存位置,确保自引用指针始终有效,避免移动导致的内存安全问题。
内容的提问来源于stack exchange,提问作者mascai
相关产品推荐
相关产品推荐

