Structuri Auto-Referențiale și Mecanismul Pinning în Rust: std::pin::Pin și Unpin în Rust

Ghid complet pentru înțelegerea tipului std::pin::Pin în Rust: prevenirea mutării în memorie a structurilor auto-referențiale, rolul fundamental în funcționarea viitorilor asincroni (Future::poll) și suprimarea trait-ului Unpin.

Sintaxă
Pin<&mut T> | Pin::new_unchecked | PhantomPinned | impl !Unpin

În modelul de memorie din Rust, toate valorile sunt implicit mutabile în memorie (Movable): compilatorul poate muta o structură dintr-un registru pe stivă sau între două locații de memorie printr-o simplă copiere binară rapidă (memcpy). Această mutare este complet sigură pentru tipurile standard, însă devine fatală pentru structurile auto-referențiale (unde un câmp conține un pointer către un alt câmp din interiorul aceleiași structuri).

Dacă o structură auto-referențială este mutată în altă zonă de memorie, pointerul intern va continua să indice către vechea adresă invalidă (provocând un Dangling Pointer). Deoarece fiecare funcție async este transformată de compilator într-o mașină de stări auto-referențială, Rust introduce tipul std::pin::Pin pentru a ancora obiectele la o adresă fizică imuabilă.

Anatomia Structurilor Auto-Referențiale și PhantomPinned
use std::marker::PhantomPinned;
use std::pin::Pin;

// Structură ce nu poate fi mutată în memorie odată inițializată:
struct NodAutoReferential {
    valoare: String,
    // Pointer către propriul câmp 'valoare':
    auto_pointer: *const String,
    // PhantomPinned anulează automat implementarea implicită a trait-ului Unpin:
    _ancora: PhantomPinned,
}

impl NodAutoReferential {
    fn new(text: &str) -> Self {
        NodAutoReferential {
            valoare: String::from(text),
            auto_pointer: std::ptr::null(),
            _ancora: PhantomPinned,
        }
    }

    fn init(self: Pin<&mut Self>) {
        let adresa_valoare = &self.valoare as *const String;
        // Modificare sigură prin unsafe deoarece obiectul este deja anulat (pinned):
        unsafe {
            let pointer_mutabil = self.get_unchecked_mut();
            pointer_mutabil.auto_pointer = adresa_valoare;
        }
    }
}

fn main() {
    // Alocăm structura pe heap într-o cutie fixată (Pinned Box):
    let mut instanta_ancorata = Box::pin(NodAutoReferential::new("Date critice"));
    instanta_ancorata.as_mut().init();

    // Încercarea de a face std::mem::swap pe 'instanta_ancorata' 
    // generează o eroare directă de compilare: trait-ul Unpin nu este implementat!
}

Diferența dintre Unpin și !Unpin

  • Unpin (Implicit): Aproape toate tipurile din Rust (i32, String, vectori) implementează automat Unpin. Pentru acestea, Pin<&mut T> este doar un ambalaj transparent și pot fi mutate fără riscuri.
  • !Unpin (Opt-out): Tipurile care conțin PhantomPinned sau stările interne generate de blocuri async renunță la Unpin. Compilatorul garantează contractual că memoria de sub pointer nu va fi niciodată mutată până la finalul duratei sale de viață.