Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the use cases of the newly proposed Pin type?

There is a new Pin type in unstable Rust and the RFC is already merged. It is said to be kind of a game changer when it comes to passing references, but I am not sure how and when one should use it.

Can anyone explain it in layman's terms?

like image 498
00imvj00 Avatar asked Apr 19 '18 06:04

00imvj00


2 Answers

What is pinning ?

In programming, pinning X means instructing X not to move.

For example:

  • Pinning a thread to a CPU core, to ensure it always executes on the same CPU,
  • Pinning an object in memory, to prevent a Garbage Collector to move it (in C# for example).

What is the Pin type about?

The Pin type's purpose is to pin an object in memory.

It enables taking the address of an object and having a guarantee that this address will remain valid for as long as the instance of Pin is alive.

What are the usecases?

The primary usecase, for which it was developed, is supporting Generators.

The idea of generators is to write a simple function, with yield, and have the compiler automatically translate this function into a state machine. The state that the generator carries around is the "stack" variables that need to be preserved from one invocation to another.

The key difficulty of Generators that Pin is designed to fix is that Generators may end up storing a reference to one of their own data members (after all, you can create references to stack values) or a reference to an object ultimately owned by their own data members (for example, a &T obtained from a Box<T>).

This is a subcase of self-referential structs, which until now required custom libraries (and lots of unsafe). The problem of self-referential structs is that if the struct move, the reference it contains still points to the old memory.

Pin apparently solves this years-old issue of Rust. As a library type. It creates the extra guarantee that as long as Pin exist the pinned value cannot be moved.

The usage, therefore, is to first create the struct you need, return it/move it at will, and then when you are satisfied with its place in memory initialize the pinned references.

like image 172
Matthieu M. Avatar answered Nov 04 '22 18:11

Matthieu M.


One of the possible uses of the Pin type is self-referencing objects; an article by ralfj provides an example of a SelfReferential struct which would be very complicated without it:

use std::ptr;
use std::pin::Pin;
use std::marker::PhantomPinned;

struct SelfReferential {
    data: i32,
    self_ref: *const i32,
    _pin: PhantomPinned,
}

impl SelfReferential {
    fn new() -> SelfReferential {
        SelfReferential { data: 42, self_ref: ptr::null(), _pin: PhantomPinned  }
    }

    fn init(self: Pin<&mut Self>) {
        let this : &mut Self = unsafe { self.get_unchecked_mut() };
        // Set up self_ref to point to this.data.
        this.self_ref = &mut this.data as *const i32;
    }

    fn read_ref(self: Pin<&Self>) -> Option<i32> {
        let this : &Self= self.get_ref();
        // Dereference self_ref if it is non-NULL.
        if this.self_ref == ptr::null() {
            None
        } else {
            Some(unsafe { *this.self_ref })
        }
    }
}

fn main() {
    let mut data: Pin<Box<SelfReferential>> = Box::new(SelfReferential::new()).into();
    data.as_mut().init();
    println!("{:?}", data.as_ref().read_ref()); // prints Some(42)
}
like image 5
ljedrz Avatar answered Nov 04 '22 18:11

ljedrz