Crate movable_ref

Source
Expand description

§tether

tether is a library for offset-based pointers, which can be used to create movable self-referential types. Ituses an offset and its current location to calculate where it points to.

§Safety

See the SelfRef type documentation for safety information.

§Features

§no_std

This crate is no_std compatible. Enable the no_std feature to use without the standard library.

§Example

Consider the memory segment below:

[.., 0x3a, 0x10, 0x02, 0xe4, 0x2b ..]

Where 0x3a has address 0xff304050 (32-bit system) and 0x2b has address 0xff304054.

If we have a 1-byte relative pointer (SelfRef<_, i8>) at address 0xff304052, then that relative pointer points to 0x2b because its address 0xff304052 plus its offset 0x02 equals 0xff304054.

Three key properties emerge:

  1. It only took 1 byte to point to another value
  2. A relative pointer can only access nearby memory
  3. If both the relative pointer and pointee move together, the relative pointer remains valid

The third property enables movable self-referential structures.

The type SelfRef<T, I> is a relative pointer where T is the target type and I is the offset storage type. In practice, you can ignore I (defaulted to isize) as it covers most use cases. For size optimization, use any type implementing Delta: i8, i16, i32, i64, i128, isize.

The tradeoff: smaller offset types reduce addressable range. isize covers at least half of addressable memory. For self-referential structures, choose an offset type whose range exceeds your structure size: std::mem::size_of::<YourStruct>() <= I::MAX.

Note: Unsized types require additional considerations.

§Self-Referential Type Example

struct SelfRefStruct {
    value: (String, u32),
    ptr: SelfRef<String, i8>
}

impl SelfRefStruct {
    pub fn new(s: String, i: u32) -> Self {
        let mut this = Self {
            value: (s, i),
            ptr: SelfRef::null()
        };

        this.ptr.set(&mut this.value.0).unwrap();
        this
    }

    pub fn fst(&self) -> &str {
        unsafe { self.ptr.as_ref_unchecked() }
    }

    pub fn snd(&self) -> u32 {
        self.value.1
    }
}

let s = SelfRefStruct::new("Hello World".into(), 10);

assert_eq!(s.fst(), "Hello World");
assert_eq!(s.snd(), 10);

let s = Box::new(s); // Force a move - relative pointers work on the heap

assert_eq!(s.fst(), "Hello World");
assert_eq!(s.snd(), 10);

§Pattern Analysis

The example demonstrates the standard pattern for safe movable self-referential types:

Structure Definition: Contains data and a relative pointer. No lifetimes are used as they would either prevent movement or create unresolvable constraints.

Initialization Pattern: Create the object with SelfRef::null(), then immediately set the pointer using SelfRef::set(). Unwrapping provides immediate feedback if the offset range is insufficient.

Movement Safety: Once set, the structure can be moved safely because relative pointers maintain their offset relationship regardless of absolute position.

Access Safety: SelfRef::as_ref_unchecked() is safe when the pointer cannot be invalidated - which occurs when direct pointer modification is impossible and field offsets remain constant after initialization.

Modules§

impls
Implementations of MetaData trait for various types
traits
Traits for metadata extraction and composition
unreachable
Module for handling unreachable code

Structs§

IntegerOffsetError
An error type for when an integer offset cannot be stored
SelfRef
A pointer that stores offsets instead of addresses, enabling movable self-referential structures.

Traits§

Nullable
A Delta type that has a null/zero value.
Offset
Trait for types that can represent pointer differences.
PointerRecomposition
The bridge that makes SelfRef work with any type, sized or unsized.

Type Aliases§

Ptr
A nullable pointer, using NonNull<T>