alias/lib.rs
1//! `alias` offers some basic ways to mutate data while
2//! aliased. [](https://crates.io/crates/alias)
3//!
4//! [*Source*](https://github.com/huonw/alias)
5//!
6//! # Examples
7//!
8//! ```rust
9//! let mut x = 0;
10//!
11//! let y = alias::one(&mut x);
12//! let z = y;
13//!
14//! // now we can read/write through multiple references
15//! z.set(10);
16//! y.set(y.get() + 2);
17//! assert_eq!(z.get(), 12);
18//! ```
19//!
20//! ```rust
21//! let mut x = [0, 0, 0, 0];
22//!
23//! let y = alias::slice(&mut x);
24//! let z = y;
25//!
26//! // now we can read/write through multiple references
27//! for i in 0..4 {
28//! z[i].set(10);
29//! y[i].set(y[i].get() + i);
30//! }
31//!
32//! assert_eq!(z[0].get(), 10);
33//! assert_eq!(z[1].get(), 11);
34//! assert_eq!(z[2].get(), 12);
35//! assert_eq!(z[3].get(), 13);
36//! ```
37//!
38//! # How is this OK?
39//!
40//! Rust's safety guarantees hinge around control how data is
41//! aliased/can be manipulated while aliased. Key to this are the `&`
42//! (shared/"immutable") and `&mut` (unique/mutable) reference types.
43//!
44//! The latter essentially has the guarantee that if `x: &mut T` is
45//! accessible, then it is the only usable path to the `T` to which it
46//! points. This ensures arbitrary mutation is entirely safe,
47//! e.g. there's no way to invalidate other references because there
48//! are no other references.
49//!
50//! On the other hand, `&T` references can be arbitrarily aliased
51//! (possibly in a large number of threads), and so mutation cannot
52//! occur by default. However, it can occur via specialised types that
53//! control what mutation can happen, such as
54//! `std::cell::Cell<T>`. That type is a plain wrapper around `T` that
55//! only works with a subset of possible `T`s (`T: Copy`). These types
56//! all assume they have full control over access to their internal
57//! data: they mediate every interaction.
58//!
59//! If one has unique access to some piece of data (`&mut T`), it is
60//! definitely safe to treat it as aliased (`&T`), but it is also safe
61//! to treat it as aliased and mutable (`&Cell<T>`). No other piece of
62//! code can be manipulating the `T` via any other path while the
63//! `&mut T` reference exists (and lifetimes ensures `&Cell<T>` cannot
64//! outlive it), so no other piece of code can do anything that
65//! violates the assumption that the `Cell` controls every
66//! interaction.
67//!
68//! This also relies on `T` → `Cell<T>` being a valid transmute, that
69//! is, the layouts being identical. Strictly speaking, this isn't
70//! guaranteed, but it is unlikely for it to remain this way. (There's
71//! an additional factor of `Cell` theoretically having more layout
72//! optimisations possible due to the way it restricts access to its
73//! internals.)
74
75use std::mem;
76use std::cell::Cell;
77
78/// Allow the mutable reference `data` to be mutated while aliased.
79///
80/// # Examples
81///
82/// ```rust
83/// let mut x = 0;
84///
85/// let y = alias::one(&mut x);
86/// let z = y;
87///
88/// // now we can read/write through multiple references
89/// z.set(10);
90/// y.set(y.get() + 2);
91/// assert_eq!(z.get(), 12);
92/// ```
93pub fn one<'a, T: Copy>(data: &'a mut T) -> &'a Cell<T> {
94 unsafe { mem::transmute(data) }
95}
96
97/// Allow the contents of the mutable slice `data` to be mutated while
98/// aliased.
99///
100/// # Examples
101///
102/// ```rust
103/// let mut x = [0, 0, 0, 0];
104///
105/// let y = alias::slice(&mut x);
106/// let z = y;
107///
108/// // now we can read/write through multiple references
109/// for i in 0..4 {
110/// z[i].set(10);
111/// y[i].set(y[i].get() + i);
112/// }
113///
114/// assert_eq!(z[0].get(), 10);
115/// assert_eq!(z[1].get(), 11);
116/// assert_eq!(z[2].get(), 12);
117/// assert_eq!(z[3].get(), 13);
118/// ```
119pub fn slice<'a, T: Copy>(data: &'a mut [T]) -> &'a [Cell<T>] {
120 unsafe { mem::transmute(data) }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
128 struct X<'a> {
129 x: u8,
130 y: u64,
131 z: i8,
132 w: &'a u32
133 }
134
135 #[test]
136 fn smoke_one() {
137 let a = 1;
138 let b = 2;
139 let val = X { x: 0xAA, y: !0, z: 0x77, w: &a };
140 let val2 = X { x: 0x33, y: !0, z: 0x55, w: &b };
141 let mut x = Some(val);
142
143 {
144 let y = one(&mut x);
145 let z = y;
146 y.set(z.get());
147 assert_eq!(y.get(), Some(val));
148 assert_eq!(z.get(), Some(val));
149
150 z.set(Some(X { x: 1, .. y.get().unwrap()}));
151 assert_eq!(y.get(), Some(X { x: 1, .. val }));
152 assert_eq!(z.get(), Some(X { x: 1, .. val }));
153
154 z.set(None);
155 assert!(y.get().is_none());
156 assert!(z.get().is_none());
157
158 y.set(Some(val2));
159 assert_eq!(y.get(), Some(val2));
160 assert_eq!(z.get(), Some(val2));
161 }
162
163 assert_eq!(x, Some(val2));
164 }
165 #[test]
166 fn smoke_slice() {
167 let a = 1;
168 let b = 2;
169 let val = X { x: 0xAA, y: !0, z: 0x77, w: &a };
170 let val2 = X { x: 0x33, y: !0, z: 0x55, w: &b };
171
172 let mut x = [Some(val), Some(val2), None];
173
174 {
175 let y = slice(&mut x);
176 let z = y;
177 assert_eq!(y.len(), 3);
178
179 let y0 = &y[0];
180 let y1 = &y[1];
181 let y2 = &y[2];
182 let z0 = &z[0];
183 let z1 = &z[1];
184 let z2 = &z[2];
185
186 y0.set(z0.get());
187 assert_eq!(y0.get(), Some(val));
188 assert_eq!(z0.get(), Some(val));
189
190 y1.set(None);
191 assert!(y1.get().is_none());
192 assert!(z1.get().is_none());
193
194 z2.set(Some(val2));
195 assert_eq!(y2.get(), Some(val2));
196 assert_eq!(z2.get(), Some(val2));
197 }
198 assert_eq!(x, [Some(val), None, Some(val2)]);
199 }
200}