movable_ref/lib.rs
1#![cfg_attr(feature = "no_std", no_std)]
2#![cfg_attr(feature = "nightly", feature(ptr_metadata))]
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. Enable the `no_std` feature to use without the standard library.
23
24## Example
25
26Consider the memory segment below:
27
28`[.., 0x3a, 0x10, 0x02, 0xe4, 0x2b ..]`
29
30Where `0x3a` has address `0xff304050` (32-bit system)
31and `0x2b` has address `0xff304054`.
32
33If we have a 1-byte relative pointer (`SelfRef<_, i8>`)
34at address `0xff304052`, then that relative pointer points to
35`0x2b` because its address `0xff304052` plus its
36offset `0x02` equals `0xff304054`.
37
38Three key properties emerge:
391) It only took 1 byte to point to another value
402) A relative pointer can only access nearby memory
413) If both the relative pointer and pointee move together,
42 the relative pointer remains valid
43
44The third property enables movable self-referential structures.
45
46The type `SelfRef<T, I>` is a relative pointer where `T` is the target type
47and `I` is the offset storage type. In practice, you can ignore `I`
48(defaulted to `isize`) as it covers most use cases. For size optimization,
49use any type implementing `Delta`: `i8`, `i16`, `i32`, `i64`, `i128`, `isize`.
50
51The tradeoff: smaller offset types reduce addressable range.
52`isize` covers at least half of addressable memory. For self-referential
53structures, choose an offset type whose range exceeds your structure size:
54`std::mem::size_of::<YourStruct>() <= I::MAX`.
55
56Note: Unsized types require additional considerations.
57
58## Self-Referential Type Example
59
60```rust
61# fn main() {
62# use movable_ref::SelfRef;
63struct SelfRefStruct {
64 value: (String, u32),
65 ptr: SelfRef<String, i8>
66}
67
68impl SelfRefStruct {
69 pub fn new(s: String, i: u32) -> Self {
70 let mut this = Self {
71 value: (s, i),
72 ptr: SelfRef::null()
73 };
74
75 this.ptr.set(&mut this.value.0).unwrap();
76 this
77 }
78
79 pub fn fst(&self) -> &str {
80 unsafe { self.ptr.as_ref_unchecked() }
81 }
82
83 pub fn snd(&self) -> u32 {
84 self.value.1
85 }
86}
87
88let s = SelfRefStruct::new("Hello World".into(), 10);
89
90assert_eq!(s.fst(), "Hello World");
91assert_eq!(s.snd(), 10);
92
93let s = Box::new(s); // Force a move - relative pointers work on the heap
94
95assert_eq!(s.fst(), "Hello World");
96assert_eq!(s.snd(), 10);
97# }
98```
99
100## Pattern Analysis
101
102The example demonstrates the standard pattern for safe movable self-referential types:
103
104**Structure Definition**: Contains data and a relative pointer. No lifetimes are used
105as they would either prevent movement or create unresolvable constraints.
106
107**Initialization Pattern**: Create the object with `SelfRef::null()`, then immediately
108set the pointer using `SelfRef::set()`. Unwrapping provides immediate feedback
109if the offset range is insufficient.
110
111**Movement Safety**: Once set, the structure can be moved safely because relative
112pointers maintain their offset relationship regardless of absolute position.
113
114**Access Safety**: `SelfRef::as_ref_unchecked()` is safe when the pointer cannot
115be invalidated - which occurs when direct pointer modification is impossible
116and field offsets remain constant after initialization.
117*/
118
119#[cfg(feature = "no_std")]
120extern crate core as std;
121
122#[cfg(test)]
123mod tests;
124
125mod error;
126mod metadata;
127mod offset;
128mod pointer;
129
130pub use self::error::*;
131pub use self::metadata::*;
132pub use self::offset::*;
133pub use self::pointer::*;