movable_ref/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(feature = "nightly", feature(ptr_metadata, strict_provenance))]
3#![allow(clippy::needless_doctest_main)]
4#![forbid(missing_docs)]
5#![deny(unused_must_use)]
6
7/*!
8# tether
9
10`tether` is a library for offset-based pointers, which can be used to create
11movable self-referential types. Ituses an offset and its current location to
12calculate where it points to.
13
14## Safety
15
16See the `SelfRef` type documentation for safety information.
17
18## Features
19
20### `no_std`
21
22This crate is `no_std` compatible. Disable the `std` feature to use without the standard library.
23
24```toml
25# For no_std environments (embedded systems, etc.)
26[dependencies]
27movable-ref = { version = "0.1.0", default-features = false }
28
29# For std environments (default)
30[dependencies]
31movable-ref = "0.1.0"
32```
33
34## Example
35
36Consider the memory segment below:
37
38`[.., 0x3a, 0x10, 0x02, 0xe4, 0x2b ..]`
39
40Where `0x3a` has address `0xff304050` (32-bit system)
41and `0x2b` has address `0xff304054`.
42
43If we have a 1-byte relative pointer (`SelfRef<_, i8>`)
44at address `0xff304052`, then that relative pointer points to
45`0x2b` because its address `0xff304052` plus its
46offset `0x02` equals `0xff304054`.
47
48Three key properties emerge:
491) It only took 1 byte to point to another value
502) A relative pointer can only access nearby memory
513) If both the relative pointer and pointee move together,
52   the relative pointer remains valid
53
54The third property enables movable self-referential structures.
55
56The type `SelfRef<T, I>` is a relative pointer where `T` is the target type
57and `I` is the offset storage type. In practice, you can ignore `I`
58(defaulted to `isize`) as it covers most use cases. For size optimization,
59use any type implementing `Delta`: `i8`, `i16`, `i32`, `i64`, `i128`, `isize`.
60
61The tradeoff: smaller offset types reduce addressable range.
62`isize` covers at least half of addressable memory. For self-referential
63structures, choose an offset type whose range exceeds your structure size:
64`std::mem::size_of::<YourStruct>() <= I::MAX`.
65
66Note: Unsized types require additional considerations.
67
68## Self-Referential Type Example
69
70```rust
71# fn main() {
72# use movable_ref::SelfRef;
73struct SelfRefStruct {
74    value: (String, u32),
75    ptr: SelfRef<String, i8>
76}
77
78impl SelfRefStruct {
79    pub fn new(s: String, i: u32) -> Self {
80        let mut this = Self {
81            value: (s, i),
82            ptr: SelfRef::null()
83        };
84
85        this.ptr.set(&mut this.value.0).unwrap();
86        this
87    }
88
89    pub fn fst(&mut self) -> &str {
90        let base = self as *const _ as *const u8;
91        unsafe { self.ptr.get_ref_from_base_unchecked(base) }
92    }
93
94    pub fn snd(&self) -> u32 {
95        self.value.1
96    }
97}
98
99let mut s = SelfRefStruct::new("Hello World".into(), 10);
100
101assert_eq!(s.fst(), "Hello World");
102assert_eq!(s.snd(), 10);
103
104let mut s = Box::new(s); // Force a move - relative pointers work on the heap
105
106assert_eq!(s.fst(), "Hello World");
107assert_eq!(s.snd(), 10);
108# }
109```
110
111## Pattern Analysis
112
113The example demonstrates the standard pattern for safe movable self-referential types:
114
115**Structure Definition**: Contains data and a relative pointer. No lifetimes are used
116as they would either prevent movement or create unresolvable constraints.
117
118**Initialization Pattern**: Create the object with `SelfRef::null()`, then immediately
119set the pointer using `SelfRef::set()`. Unwrapping provides immediate feedback
120if the offset range is insufficient.
121
122**Movement Safety**: Once set, the structure can be moved safely because relative
123pointers maintain their offset relationship regardless of absolute position.
124
125**Access Safety**: `SelfRef::as_ref_unchecked()` is safe when the pointer cannot
126be invalidated - which occurs when direct pointer modification is impossible
127and field offsets remain constant after initialization.
128
129## Failure Modes
130
131* Calling unchecked APIs such as [`SelfRef::get_ref_from_base_unchecked`] before
132  initialisation is undefined behaviour; prefer the safe
133  [`SelfRefCell::try_get`] wrappers to detect readiness.
134* When the optional `debug-guards` feature is enabled, absolute pointers captured
135  through [`SelfRef::from_parts_with_target`] or [`SelfRef::guard`] must only be
136  used while the owning structure remains at a fixed address. Moving the container
137  without refreshing these guards will trigger debug assertions.
138*/
139
140#[cfg(not(feature = "std"))]
141extern crate core as std;
142
143#[cfg(test)]
144mod tests;
145
146mod error;
147mod macros;
148mod metadata;
149mod offset;
150mod pointer;
151mod combinators {
152    pub mod self_ref_cell;
153}
154
155pub use self::combinators::self_ref_cell::SelfRefCell;
156pub use self::error::*;
157pub use self::metadata::*;
158pub use self::offset::*;
159pub use self::pointer::*;