一、PIN和Safe

其实PIN这个东西,和Safe你说关系大也不多大,你说小也不多小。其实Rust语言搞得声势这么大,就出来一个问题,很多小细节和它所说的一些安全是无法自然衔接的(请注意,是自然)。那么为了解决这些问题,就必须出现 一系列的各种小技巧或者说小办法,这个Pin其实就是其中的一类。
在c++或者其它一些语言中,是有深拷贝和浅拷贝之说的,在Linux中有Cow一说,这些内存处理的情形,或多或少和这个Pin有一些相通之处。在Rust中,很多情况下,其实Pin是用不到的。因为大多数情形下,都实现Unpin Traits,也就是说,对是否使用Pin都无所顾忌。但是在引入的async/await编程中,出现了一类问题,Futrue的移动,导致内部的指针的指针内容的不一致,会产生一些异想不到的后果,这时候Pin就可以上场了。
那么这样来看,其实Pin还是用来Safe操作代码的一种补救措施。或者说,可以认为是Safe动作的一种额外的处理方式。

二、应用场景

在上面其实已经提到了应用场景,就是在移动数据时,内部有数据指针的情况。这里典型就是异步操作时的数据结构的移动。Pin的作用就是固定住指针指针向的内存,在指针被移动时,保证指针指向的内存地址的一致性。这样说可能不好理解,看一个图就明白了:

rustdesk 不用密码就能连接_rust

对Unpin取反,!Unpin的双重否定就是pin。如果一个类型中包含了PhantomPinned,那么这个类型就是!Unpin
其实有Pin就会有UnPin,就会有!Unpin(双重否定就是Pin),Pin的形式如Pin<P>作用就是将指针P指向的内存T锁死,不能移动。即无法使用safe代码获得&mut T。那么Unpin就恰好相反了。而!Unpin其实就是使用了PhantomPinned,它可以实现Unpin变为Pinned。

三、实例分析

use std::pin::Pin;

#[derive(Debug)]
struct Test {
    a: String,
    b: *const String,
}

impl Test {
    fn new(txt: &str) -> Self {
        Test {
            a: String::from(txt),
            b: std::ptr::null(),
        }
    }

    fn init(&mut self) {
        let self_ref: *const String = &self.a;
        self.b = self_ref;
    }

    fn a(&self) -> &str {
        &self.a
    }

    fn b(&self) -> &String {
        unsafe {&*(self.b)}
    }
}
fn main() {
    let mut test1 = Test::new("test1");
    test1.init();
    let mut test2 = Test::new("test2");
    test2.init();

    println!("a: {}, b: {}", test1.a(), test1.b());
   //重点看一下这个交换后打印的动作
    std::mem::swap(&mut test1, &mut test2);
    println!("a: {}, b: {}", test2.a(), test2.b());

}

运行结果是:

a: test1, b: test1
a: test1, b: test2  -----这里是不是和想象的有所不同?

如果一些函数调用中需要对Pin进行转换有如下两种方式(代码自令狐一冲的知乎:https://zhuanlan.zhihu.com/p/157348723):

use pin_utils::pin_mut; // `pin_utils` is a handy crate available on crates.io

// A function which takes a `Future` that implements `Unpin`.
fn execute_unpin_future(x: impl Future<Output = ()> + Unpin) { /* ... */ }

let fut = async { /* ... */ };
execute_unpin_future(fut); // Error: `fut` does not implement `Unpin` trait

// Pinning with `Box`:使用Box
let fut = async { /* ... */ };
let fut = Box::pin(fut);
execute_unpin_future(fut); // OK

// Pinning with `pin_mut!`:使用这个宏
let fut = async { /* ... */ };
pin_mut!(fut);
execute_unpin_future(fut); // OK

需要说明的是,在栈上Pin是不推荐的,有风险的,这个应该大家都明白。而固定到堆上没有什么问题的,不过需要注意生命周期的过程管理。一般来说,除了Future::poll中使用,不建议在其它方面使用这个Pin,就当不存在吧。当然,也可以使用Drop关键字来强制处理Pin的数据,但实际上这违反了Pin的约束,有点小复杂了。

四、总结

Rust能否取代c++,还需要继续看,不过在一些领域Rust确实是有它的优势的,开放包容,兼容并蓄,才是一个真正的技术者的心态。保守和固步自封,或者