movable-ref 0.2.0

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

#[cfg(feature = "std")]
use std::string::String;

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

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

impl<T, U: ?Sized + PointerRecomposition> SelfRefTest<T, U> {
    pub fn new(t: T, f: fn(&mut T) -> &mut U) -> Self {
        let mut this = Self {
            t,
            t_ref: SelfRef::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(&mut self) -> &U {
        unsafe {
            self.t_ref
                .get_ref_from_base_unchecked(self as *const _ as *const u8)
        }
    }

    #[allow(unused)]
    pub fn t_ref_mut(&mut self) -> &mut U {
        let base = self as *mut _ as *mut u8;
        unsafe { self.t_ref.get_mut_from_base_unchecked(base) }
    }
}

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

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

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

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

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

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

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

    let mut s = block_opt(s);

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

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

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

    let mut s = block_opt(s);

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

#[test]
fn swap() {
    let mut s = SelfRefTest::new("Hello World", id);
    let mut x = SelfRefTest::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 = SelfRefTest::new("Hello World", id);

    *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: SelfRefTest<[u8; 5], [u8]>) {
        let mut s = s;
        assert_eq!(*s.t(), [0, 1, 2, 3, 4]);
        assert_eq!(*s.t_ref(), [2, 3, 4]);
    }

    let mut s = SelfRefTest::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);
}

#[cfg(feature = "std")]
#[test]
fn equality_tracks_metadata() {
    let left = SelfRefTest::new([0, 1, 2, 3, 4], |x| &mut x[..]);
    let right = SelfRefTest::new([0, 1, 2, 3, 4], |x| &mut x[1..]);

    assert_eq!(
        left.t_ref,
        SelfRefTest::new([9, 8, 7, 6, 5], |x| &mut x[..]).t_ref
    );
    assert_ne!(left.t_ref, right.t_ref);
}

#[cfg(feature = "std")]
#[test]
fn from_parts_restores_pointer() {
    let mut node = SelfRefTest::new([5, 6, 7, 8, 9], |x| &mut x[1..]);
    let (stored_offset, stored_components) = node
        .t_ref
        .parts_if_ready()
        .expect("pointer should be ready");
    node.t_ref = SelfRef::from_parts(stored_offset, stored_components);

    assert!(node.t_ref.is_ready());
    assert_eq!(*node.t_ref(), [6, 7, 8, 9]);
}

#[cfg(feature = "std")]
#[test]
fn try_accessors() {
    struct Wrapper {
        cell: SelfRefCell<String, i16>,
    }

    let wrapper = Wrapper {
        cell: SelfRefCell::new(String::from("alive")).unwrap(),
    };

    assert_eq!(wrapper.cell.try_get().map(|value| value.len()), Some(5));
}

#[test]
fn check_copy() {
    fn is_copy<T: Copy>() {}

    #[allow(unused, path_statements)]
    fn check<T: ?Sized + PointerRecomposition, I: Offset>() {
        is_copy::<SelfRef<T, I>>;
    }
}

#[cfg(feature = "nightly")]
mod nightly {
    use super::*;

    #[derive(Debug)]
    struct TestStruct {
        value: u32,
    }

    #[test]
    fn check_trait_object_simple() {
        let mut s = SelfRefTest::new(TestStruct { value: 42 }, |x| unsafe {
            TraitObject::from_mut(x as &mut dyn std::fmt::Debug)
        });

        assert_eq!(s.t().value, 42);

        #[cfg(feature = "std")]
        {
            let debug_str = format!("{:?}", s.t_ref().as_ref());
            assert!(debug_str.contains("42"));
        }
    }

    #[test]
    fn check_trait_object_after_move() {
        let mut s = SelfRefTest::new(TestStruct { value: 42 }, |x| unsafe {
            TraitObject::from_mut(x as &mut dyn std::fmt::Debug)
        });

        assert_eq!(s.t().value, 42);

        #[cfg(feature = "std")]
        {
            let debug_str = format!("{:?}", s.t_ref().as_ref());
            assert!(debug_str.contains("42"));
        }

        #[inline(never)]
        fn force_move<T>(t: T) -> T {
            t
        }

        let mut s = force_move(s);

        assert_eq!(s.t().value, 42);

        #[cfg(feature = "std")]
        {
            let debug_str = format!("{:?}", s.t_ref().as_ref());
            assert!(debug_str.contains("42"));
        }
    }

    #[test]
    #[cfg(feature = "std")]
    fn check_trait_object_after_move_heap() {
        let mut s = SelfRefTest::new(TestStruct { value: 42 }, |x| unsafe {
            TraitObject::from_mut(x as &mut dyn std::fmt::Debug)
        });

        assert_eq!(s.t().value, 42);

        #[cfg(feature = "std")]
        {
            let debug_str = format!("{:?}", s.t_ref().as_ref());
            assert!(debug_str.contains("42"));
        }

        let mut s = Box::new(s);

        assert_eq!(s.t().value, 42);

        #[cfg(feature = "std")]
        {
            let debug_str = format!("{:?}", s.t_ref().as_ref());
            assert!(debug_str.contains("42"));
        }
    }
}