Skip to main content

indigo_wrap/
lib.rs

1/*!
2
3Ever wish you could make a reference type wrapper generic?
4
5```compile_fail
6struct Foo<W>(W<usize>);
7type SharedFoo = Foo<Arc>; // would become `Foo(Arc<usize>)`
8```
9
10Ever wish Rust had "mutability generics"?
11
12```compile_fail
13struct Bar<'a, M>(&'a M usize);
14type BarRef<'a> = Bar<'a, ref>; // would become `Bar<'a>(&'a usize)`
15type BarMut<'a> = Bar<'a, mut>; // would become `Bar<'a>(&'a mut usize)`
16```
17
18Well please keep wishing, because I want those too...
19
20But in the meantime, this crate provides a hack to emulate this behavior in userland!
21
22```no_run
23# use indigo_wrap::*;
24struct Foo<'a, W: Wrap>(W::Wrap<'a, usize>);
25type SharedFoo = Foo<'static, WArc>; // actually becomes `Foo(Arc<usize>)`
26```
27
28```no_run
29# use indigo_wrap::*;
30struct Bar<'a, W: WrapRef>(W::WrapRef<'a, usize>);
31type BarRef<'a> = Bar<'a, WRef>; // actually becomes `Bar<'a>(&'a usize)`
32type BarMut<'a> = Bar<'a, WMut>; // actually becomes `Bar<'a>(&'a mut usize)`
33```
34
35This is a limited form of higher-kinded types, like you find in languages like Haskell.
36
37## Example
38
39Here's an example of a cursor type like you might find in a simple text editor. It maintains its own
40state, but whether or not it's valid is in relation to the string it's an offset in.
41
42<details>
43
44<summary>Show code</summary>
45
46```no_run
47# use indigo_wrap::*;
48// Use the weakest trait bound `Wrap` when defining the type to perform wrapping:
49struct CursorView<'a, W: Wrap> {
50    text: W::Wrap<'a, String>,
51    byte_offset: usize,
52}
53
54type RawCursor = CursorView<'static, WPhantomData>;
55
56type Cursor<'a> = CursorView<'a, WRef>;
57
58type CursorEdit<'a> = CursorView<'a, WMut>;
59
60// ...or when you don't need to touch the wrapped type:
61impl<W: Wrap> CursorView<'_, W> {
62    fn reset(&mut self) {
63        self.byte_offset = 0;
64    }
65}
66
67// Use the stronger trait bound `WrapRef` if you need to read values:
68impl<W: WrapRef> CursorView<'_, W> {
69    fn is_empty(&self) -> bool {
70        // We can call `String::is_empty` transparently because `WrapRef` implies `Deref`:
71        self.text.is_empty()
72    }
73}
74
75// Use the strongest trait bound `WrapMut` if you need to mutate values:
76impl<W: WrapMut> CursorView<'_, W> {
77    fn insert(&mut self, text: &str) {
78        // We can call `String::insert_str` transparently because `WrapMut` implies `DerefMut`:
79        self.text.insert_str(self.byte_offset, text);
80    }
81}
82```
83
84</details>
85
86There are certain operations you can perform:
87
88- ...without seeing the string (e.g. `RawCursor` could safely set `byte_offset` to 0).
89- ...while holding an immutable reference (e.g. checking whether the string is empty).
90- ...while holding a mutable reference (e.g. inserting text into the string at the byte offset).
91
92These are captured with the [`Wrap`], [`WrapRef`], and [`WrapMut`] traits respectively.
93
94If you write generic `impl` blocks with trait bounds on the reference type wrapper (e.g.
95`impl<W: WrapMut> CursorView<'_, W>`) rather than hardcoding it (e.g. `impl CursorView<'_, WMut>`),
96you get a kind of subtyping/inheritance thing where the most powerful versions of your type (e.g.
97`CursorEdit`) have all the methods available, but your weaker versions (e.g. `Cursor`) only get the
98subset that apply to their wrapped type.
99
100*/
101
102use std::{
103    cell::{Ref, RefMut},
104    marker::PhantomData,
105    ops::{Deref, DerefMut},
106    rc::Rc,
107    sync::{Arc, MutexGuard},
108};
109
110/// Generic reference type wrappers
111///
112/// This is a generalized version of [`WrapRef`] that doesn't require [`Deref`], so you can use
113/// wrappers that may not have the type they're wrapping.
114///
115/// If your wrapper type implements [`Deref`], you should implement the [`WrapRef`] trait instead,
116/// and you'll get a [`Wrap`] impl for free.
117pub trait Wrap {
118    type Wrap<'a, T: ?Sized + 'a>;
119}
120
121/// Wrap with [`PhantomData`]
122pub enum WPhantomData {}
123
124/// Wrap with [`PhantomData`]
125impl Wrap for WPhantomData {
126    type Wrap<'a, T: ?Sized + 'a> = PhantomData<T>;
127}
128
129/// Anything implementing [`WrapRef`] trivially implements [`Wrap`].
130#[diagnostic::do_not_recommend]
131impl<R: WrapRef> Wrap for R {
132    type Wrap<'a, T: ?Sized + 'a> = R::WrapRef<'a, T>;
133}
134
135/// Generic immutable/shared reference type wrappers
136///
137/// Specialized version of `Wrap` that requires [`Deref`], so you can read the underlying `T` even
138/// if the wrapper type is polymorphic.
139///
140/// If your wrapper type doesn't implement [`Deref`], you should implement the [`Wrap`] trait
141/// instead.
142pub trait WrapRef {
143    type WrapRef<'a, T: ?Sized + 'a>: Deref<Target = T>;
144}
145
146/// Wrap with [`&`]
147pub enum WRef {}
148
149/// Wrap with [`&`]
150impl WrapRef for WRef {
151    type WrapRef<'a, T: ?Sized + 'a> = &'a T;
152}
153
154/// Wrap with [`Ref`]
155pub enum WRefCell {}
156
157/// Wrap with [`Ref`]
158impl WrapRef for WRefCell {
159    type WrapRef<'a, T: ?Sized + 'a> = Ref<'a, T>;
160}
161
162/// Wrap with [`Rc`]
163pub enum WRc {}
164
165/// Wrap with [`Rc`]
166impl WrapRef for WRc {
167    type WrapRef<'a, T: ?Sized + 'a> = Rc<T>;
168}
169
170/// Wrap with [`Arc`]
171pub enum WArc {}
172
173/// Wrap with [`Arc`]
174impl WrapRef for WArc {
175    type WrapRef<'a, T: ?Sized + 'a> = Arc<T>;
176}
177
178/// Anything implementing [`WrapMut`] trivially implements [`WrapRef`].
179#[diagnostic::do_not_recommend]
180impl<R: WrapMut> WrapRef for R {
181    type WrapRef<'a, T: ?Sized + 'a> = R::WrapMut<'a, T>;
182}
183
184/// Generic exclusive/mutable reference type wrappers
185///
186/// Specialized version of [`Wrap`] that requires [`DerefMut`], so you can mutate the underlying `T`
187/// even if the wrapper type is polymorphic.
188///
189/// If your wrapper type doesn't implement [`DerefMut`], you should implement the [`WrapRef`] trait
190/// instead.
191pub trait WrapMut {
192    type WrapMut<'a, T: ?Sized + 'a>: DerefMut<Target = T>;
193}
194
195/// Wrap with [`&mut`]
196pub enum WMut {}
197
198/// Wrap with [`&mut`]
199impl WrapMut for WMut {
200    type WrapMut<'a, T: ?Sized + 'a> = &'a mut T;
201}
202
203/// Wrap with [`RefMut`]
204pub enum WRefCellMut {}
205
206/// Wrap with [`RefMut`]
207impl WrapMut for WRefCellMut {
208    type WrapMut<'a, T: ?Sized + 'a> = RefMut<'a, T>;
209}
210
211/// Wrap with [`MutexGuard`]
212pub enum WMutexGuard {}
213
214/// Wrap with [`MutexGuard`]
215impl WrapMut for WMutexGuard {
216    type WrapMut<'a, T: ?Sized + 'a> = MutexGuard<'a, T>;
217}
218
219/// Wrap with [`Box`]
220pub enum WBox {}
221
222/// Wrap with [`Box`]
223impl WrapMut for WBox {
224    type WrapMut<'a, T: ?Sized + 'a> = Box<T>;
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230    use std::convert::Infallible;
231
232    struct TestDeref<T> {
233        value: T,
234    }
235
236    #[test]
237    fn test_wrap() {
238        let _: <WPhantomData as Wrap>::Wrap<'_, Infallible> = PhantomData;
239
240        let xs: [usize; 3] = [1, 2, 3];
241        let _: <WRef as Wrap>::Wrap<'_, [usize]> = &xs;
242
243        let mut xs: [usize; 3] = [1, 2, 3];
244        let _: <WMut as Wrap>::Wrap<'_, [usize]> = &mut xs;
245
246        let mut x: TestDeref<Vec<usize>> = TestDeref { value: vec![42] };
247        let r: <WMut as Wrap>::Wrap<'_, TestDeref<Vec<usize>>> = &mut x;
248        assert_eq!(r.value[0], 42);
249    }
250
251    #[test]
252    fn test_wrap_ref() {
253        let xs: [usize; 3] = [1, 2, 3];
254        let _: <WRef as WrapRef>::WrapRef<'_, [usize]> = &xs;
255
256        let mut x: TestDeref<Vec<usize>> = TestDeref { value: vec![42] };
257        let r: <WRef as WrapRef>::WrapRef<'_, TestDeref<Vec<usize>>> = &mut x;
258        assert_eq!(r.value[0], 42);
259    }
260
261    #[test]
262    fn test_wrap_mut() {
263        let mut xs: [usize; 3] = [1, 2, 3];
264        let _: <WMut as WrapMut>::WrapMut<'_, [usize]> = &mut xs;
265
266        let xs: [usize; 3] = [1, 2, 3];
267        let mut bxs: <WBox as WrapMut>::WrapMut<'_, [usize]> = Box::new(xs);
268        bxs[1] = 42;
269        assert_eq!(bxs[1], 42);
270
271        let mut x: TestDeref<Vec<usize>> = TestDeref { value: vec![42] };
272        let r: <WMut as WrapMut>::WrapMut<'_, TestDeref<Vec<usize>>> = &mut x;
273        r.value[0] = 69;
274    }
275}