movable-ref 0.2.0

A tool for building movable self-referential types
Documentation
#![allow(clippy::uninlined_format_args)]

use movable_ref::{selfref_accessors, SelfRef, SelfRefCell};
use std::hint::black_box;
use std::time::Instant;

struct SelfRefRelPtr {
    data: [u64; 100],
    ptr: SelfRefCell<u64, i16>,
}

impl SelfRefRelPtr {
    fn new() -> Self {
        let mut this = Self {
            data: [0u64; 100],
            ptr: SelfRefCell::new(0u64).unwrap(),
        };

        this.data.iter_mut().enumerate().for_each(|(i, item)| {
            *item = i as u64 * 2;
        });

        this.ptr = SelfRefCell::new(this.data[50]).unwrap();
        this
    }
}

selfref_accessors!(impl SelfRefRelPtr { get_value : ptr -> u64 });

struct DirectAccess {
    data: [u64; 100],
    index: usize,
}

impl DirectAccess {
    fn new() -> Self {
        let mut data = [0u64; 100];
        data.iter_mut().enumerate().for_each(|(i, item)| {
            *item = i as u64 * 2;
        });

        Self { data, index: 50 }
    }

    fn get_value(&self) -> u64 {
        self.data[self.index]
    }
}

fn benchmark_memory_usage() {
    println!("Memory Usage Comparison:");
    println!(
        "  SelfRef<u64, i8>:   {} bytes",
        std::mem::size_of::<SelfRef<u64, i8>>()
    );
    println!(
        "  SelfRef<u64, i16>:  {} bytes",
        std::mem::size_of::<SelfRef<u64, i16>>()
    );
    println!(
        "  SelfRef<u64, i32>:  {} bytes",
        std::mem::size_of::<SelfRef<u64, i32>>()
    );
    println!(
        "  SelfRef<u64, isize>:{} bytes",
        std::mem::size_of::<SelfRef<u64, isize>>()
    );
    println!(
        "  *const u64:        {} bytes",
        std::mem::size_of::<*const u64>()
    );
    println!(
        "  usize (index):     {} bytes",
        std::mem::size_of::<usize>()
    );
    println!();

    println!("Structure Size Comparison:");
    println!(
        "  SelfRefRelPtr:     {} bytes",
        std::mem::size_of::<SelfRefRelPtr>()
    );
    println!(
        "  DirectAccess:      {} bytes",
        std::mem::size_of::<DirectAccess>()
    );
    println!();
}

fn benchmark_access_performance() {
    const ITERATIONS: usize = 10_000_000;

    let rel_ptr_struct = SelfRefRelPtr::new();
    let direct_struct = DirectAccess::new();

    let start = Instant::now();
    (0..ITERATIONS).for_each(|_| {
        black_box(rel_ptr_struct.get_value());
    });
    let rel_ptr_time = start.elapsed();

    let start = Instant::now();
    (0..ITERATIONS).for_each(|_| {
        black_box(direct_struct.get_value());
    });
    let direct_time = start.elapsed();

    println!("Access Performance ({} iterations):", ITERATIONS);
    println!("  SelfRef access:    {:?}", rel_ptr_time);
    println!("  Direct access:     {:?}", direct_time);
    println!(
        "  Overhead:          {:.2}%",
        ((rel_ptr_time.as_nanos() as f64 / direct_time.as_nanos() as f64) - 1.0) * 100.0
    );
    println!();
}

fn demonstrate_movability() {
    println!("Movability Demonstration:");

    let s = SelfRefRelPtr::new();
    println!("  Original value: {}", s.get_value());

    let boxed = Box::new(s);
    println!("  After Box::new: {}", boxed.get_value());

    let moved_again = *boxed;
    println!("  After unbox:    {}", moved_again.get_value());

    let vec = [moved_again, SelfRefRelPtr::new()];
    println!("  In vector[0]:   {}", vec[0].get_value());
    println!("  In vector[1]:   {}", vec[1].get_value());

    println!("  ✓ All moves preserved the internal reference!");
    println!();
}

fn main() {
    println!("rel-ptr Performance Benchmarks");
    println!("==============================");
    println!();

    benchmark_memory_usage();
    benchmark_access_performance();
    demonstrate_movability();

    println!("Metrics:");
    println!("- SelfRef uses 1-8 bytes vs 8 bytes for full pointers (on 64-bit)");
    println!("- Access overhead is minimal (usually <50% vs direct access)");
    println!("- Enables impossible patterns: movable self-referential structures");
    println!("- Zero-cost abstraction: overhead comes from offset calculation");
    println!("- Perfect for embedded systems where every byte counts");
}