rel-ptr 0.2.3

A tool for building movable self-referential types
Documentation
use super::*;

struct SelfRef<T, U: ?Sized + MetaData> {
    t_ref: RelPtr<U, i8>,
    t: T,
}

fn id<T>(t: &mut T) -> &mut T {
    t
}

impl<T, U: ?Sized + MetaData> SelfRef<T, U> {
    pub fn new(t: T, f: fn(&mut T) -> &mut U) -> Self {
        let mut this = Self {
            t: t.into(),
            t_ref: RelPtr::null(),
        };

        this.t_ref.set(f(&mut this.t)).unwrap();

        this
    }

    pub fn t(&self) -> &T {
        &self.t
    }

    pub fn t_mut(&mut self) -> &mut T {
        &mut self.t
    }

    pub fn t_ref(&self) -> &U {
        unsafe { self.t_ref.as_ref_unchecked() }
    }

    #[allow(unused)]
    pub fn t_ref_mut(&mut self) -> &mut U {
        unsafe { self.t_ref.as_mut_unchecked() }
    }
}

#[inline(never)]
fn block_opt<T>(x: T) -> T {
    x
}

#[test]
fn simple_test() {
    let mut s = SelfRef {
        t: "Hello World",
        t_ref: RelPtr::null(),
    };

    s.t_ref.set(&mut s.t).unwrap();

    assert_eq!(s.t(), s.t_ref());
    assert_eq!(*s.t(), "Hello World");
    assert_eq!(*s.t_ref(), "Hello World");
}

#[test]
fn simple_move() {
    let mut s = SelfRef {
        t: "Hello World",
        t_ref: RelPtr::null(),
    };

    s.t_ref.set(&mut s.t).unwrap();

    assert_eq!(s.t(), s.t_ref());
    assert_eq!(*s.t(), "Hello World");
    assert_eq!(*s.t_ref(), "Hello World");

    let s = block_opt(s);

    assert_eq!(s.t(), s.t_ref());
    assert_eq!(*s.t(), "Hello World");
    assert_eq!(*s.t_ref(), "Hello World");
}

#[test]
fn simple_move_after_init() {
    let s = SelfRef::new("Hello World", id);

    assert_eq!(s.t(), s.t_ref());
    assert_eq!(*s.t(), "Hello World");
    assert_eq!(*s.t_ref(), "Hello World");

    let s = block_opt(s);

    assert_eq!(s.t(), s.t_ref());
    assert_eq!(*s.t(), "Hello World");
    assert_eq!(*s.t_ref(), "Hello World");
}

#[test]
fn swap() {
    let mut s = SelfRef::new("Hello World", id);
    let mut x = SelfRef::new("Killer Move", id);

    assert_eq!(*s.t(), "Hello World");
    assert_eq!(*x.t(), "Killer Move");

    assert_eq!(*s.t_ref(), "Hello World");
    assert_eq!(*x.t_ref(), "Killer Move");

    std::mem::swap(&mut s, &mut x);

    assert_eq!(*s.t(), "Killer Move");
    assert_eq!(*x.t(), "Hello World");

    assert_eq!(*s.t_ref(), "Killer Move");
    assert_eq!(*x.t_ref(), "Hello World");
}

#[test]
fn aliasing() {
    let mut s = SelfRef::new("Hello World", id);

    assert_eq!(s.t(), s.t_ref());

    *s.t_mut() = "Killer Move";

    assert_eq!(*s.t(), "Killer Move");
    assert_eq!(*s.t_ref(), "Killer Move");
}

#[test]
fn sub_str() {
    #[inline(never)]
    fn get_move(s: SelfRef<[u8; 5], [u8]>) {
        assert_eq!(*s.t(), [0, 1, 2, 3, 4]);
        assert_eq!(*s.t_ref(), [2, 3, 4]);
    }

    let s = SelfRef::new([0, 1, 2, 3, 4], |x| &mut x[2..]);

    assert_eq!(*s.t(), [0, 1, 2, 3, 4]);
    assert_eq!(*s.t_ref(), [2, 3, 4]);

    get_move(s);
}

#[test]
fn check_copy() {
    fn is_copy<T: Copy>() {}
    
    #[allow(unused, path_statements)]
    fn check<T: ?Sized + MetaData, I: Delta>() {
        is_copy::<RelPtr<T, I>>;
    }
}

#[cfg(feature = "nightly")]
mod nightly {
    use super::*;
    
    #[test]
    fn check_trait_object_simple() {
        let s = SelfRef::<[u8; 5], TraitObject<dyn PartialEq<[u8]>>>::new(
            [0, 1, 2, 3, 4],
            |x| unsafe {
                let x = &mut *(&mut x[2..] as *mut [u8] as *mut [u8; 3]);
                TraitObject::from_mut(x)
            }

        );

        assert_eq!(*s.t(), [0, 1, 2, 3, 4]);

        let eq: &[u8] = &[2, 3, 4];
        assert!(s.t_ref().as_ref() == eq);
    }

    #[test]
    fn check_trait_object_after_move() {
        let s = SelfRef::<[u8; 5], TraitObject<dyn PartialEq<[u8]>>>::new(
            [0, 1, 2, 3, 4],
            |x| unsafe {
                let x = &mut *(&mut x[2..] as *mut [u8] as *mut [u8; 3]);
                TraitObject::from_mut(x)
            }

        );

        assert_eq!(*s.t(), [0, 1, 2, 3, 4]);

        let eq: &[u8] = &[2, 3, 4];
        assert!(s.t_ref().as_ref() == eq);
    
        #[inline(never)]
        fn force_move<T>(t: T) -> T {
            t
        }

        let s = force_move(s);

        assert_eq!(*s.t(), [0, 1, 2, 3, 4]);

        assert!(s.t_ref().as_ref() == eq);
    }

    #[test]
    #[cfg(not(feature = "no_std"))]
    fn check_trait_object_after_move_heap() {
        let s = SelfRef::<[u8; 5], TraitObject<dyn PartialEq<[u8]>>>::new(
            [0, 1, 2, 3, 4],
            |x| unsafe {
                let x = &mut *(&mut x[2..] as *mut [u8] as *mut [u8; 3]);
                TraitObject::from_mut(x)
            }

        );

        assert_eq!(*s.t(), [0, 1, 2, 3, 4]);

        let eq: &[u8] = &[2, 3, 4];
        assert!(s.t_ref().as_ref() == eq);

        let s = Box::new(s);

        assert_eq!(*s.t(), [0, 1, 2, 3, 4]);

        let eq: &[u8] = &[2, 3, 4];
        assert!(s.t_ref().as_ref() == eq);
    }
}