call_by/lib.rs
1//! # Calling Convention Polymorphism in Rust
2//!
3//! To parameterize a function by calling convention, we can specify that it takes some `T:
4//! By<'a, Convention>`, and say that its input is of type `<T as By<'a,
5//! Convention>>::Type`. This is essentially a defunctionalization of Rust's reference operators.
6
7//! This trick can be used to permit the *implementor* of a trait to pick the calling convention for
8//! a value passed into (or out of) a function defined in that trait, rather than this being
9//! hardcoded in the trait definition.
10
11//! ## Examples
12
13//! For instance, say we wanted to define an abstraction for channels that can send values. Imagine,
14//! however, that some channels might need to take ownership of the values they send, while others
15//! might serialize values given only a reference to that value. In order to unify these two notions
16//! into one trait, we can parameterize over the calling convention for the input value:
17
18//! ```rust
19//! use call_by::{By, Convention};
20
21//! trait Sender<'a, T>
22//! where
23//! T: By<'a, Self::Convention>,
24//! {
25//! type Convention: Convention;
26//! fn send(&self, value: <T as By<'a, Self::Convention>>::Type);
27//! }
28//! ```
29
30//! Implementers of the `Sender` trait can choose whether the associated type `Convention` should be
31//! `Val`, `Ref`, or `Mut`, which toggles the result of `<T as By<'a, Self::Convention>>::Type`
32//! between `T`, `&'a T`, and `&'a mut T`, respectively. Meanwhile, callers of the `send` method on
33//! concretely known types don't need to specify the calling convention; the type-level function
34//! determines what type they need to pass as the argument to `send`, and type errors are reported
35//! in reference to that concrete type if it is known at the call site.
36//! <!-- snip -->
37use std::{mem, ptr};
38
39/// There are three fundamental ways to pass a `T` as input or return a `T` as output: by [`Val`]ue,
40/// by shared immutable [`Ref`]erence, and by unique [`Mut`]able reference.
41///
42/// This is a sealed trait, implemented for all three of these conventions.
43pub trait Convention: sealed::Convention + Sized {
44 const TOKEN: Self;
45}
46
47impl Convention for Val {
48 const TOKEN: Self = Val;
49}
50
51impl Convention for Ref {
52 const TOKEN: Self = Ref;
53}
54
55impl Convention for Mut {
56 const TOKEN: Self = Mut;
57}
58
59/// To get the type of `T` via calling convention `Convention`, write `<T as By<'a,
60/// Convention>>::Type`.
61pub trait By<'a, C: Convention> {
62 /// The type of `Self` when called by `Convention`.
63 type Type;
64
65 /// Copy a thing of unknown calling convention, returning an owned value.
66 fn copy(this: Self::Type) -> Self
67 where
68 Self: Copy;
69
70 /// Clone a thing of unknown calling convention, returning an owned value.
71 fn clone(this: Self::Type) -> Self
72 where
73 Self: Clone;
74}
75
76/// Taking a `T` by [`Val`]ue means taking a `T` as input to or output from a function.
77#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
78pub struct Val;
79
80impl<'a, T> By<'a, Val> for T {
81 type Type = T;
82
83 fn copy(this: Self::Type) -> Self
84 where
85 Self: Copy,
86 {
87 this
88 }
89
90 fn clone(this: Self::Type) -> Self
91 where
92 Self: Clone,
93 {
94 this
95 }
96}
97
98/// Taking a `T` by [`Ref`]erence means taking `&'a T` as input to or output from a function.
99#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
100pub struct Ref;
101
102impl<'a, T: 'a + ?Sized> By<'a, Ref> for T {
103 type Type = &'a T;
104
105 fn copy(this: Self::Type) -> Self
106 where
107 Self: Copy,
108 {
109 *this
110 }
111
112 fn clone(this: Self::Type) -> Self
113 where
114 Self: Clone,
115 {
116 this.clone()
117 }
118}
119
120/// Taking a `T` by [`Mut`]able reference means taking `&'a mut T` as input to or output from a
121/// function.
122#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
123pub struct Mut;
124
125impl<'a, T: 'a + ?Sized> By<'a, Mut> for T {
126 type Type = &'a mut T;
127
128 fn copy(this: Self::Type) -> Self
129 where
130 Self: Copy,
131 {
132 *this
133 }
134
135 fn clone(this: Self::Type) -> Self
136 where
137 Self: Clone,
138 {
139 this.clone()
140 }
141}
142
143/// Convert between different calling conventions.
144///
145/// Only some conversions are sensible in Rust, due to the ownership system. These are the valid
146/// conversions, with the constraints on the underlying type `T` noted:
147///
148/// | Can I convert... | ... to [`Val`] (`T`) | ... to [`Ref`] (`&'a T`) | ... to [`Mut`] (`&'a mut T`) |
149/// | :--------------------------------- | :------------------ | :------ | :------ |
150/// | **from [`Val`] (`T`) ...** | (valid for all `T`) | ❌* | ❌* |
151/// | **from [`Ref`] (`&'a T`) ...** | `T: 'a +` [`Clone`] | `T: 'a` | ❌** |
152/// | **from [`Mut`] (`&'a mut T`) ...** | `T: 'a +` [`Clone`] | `T: 'a` | `T: 'a` |
153///
154/// > \* Impossible because references can't outlive the data they borrow.
155/// >
156/// > \** Impossible because potentially-aliased data can't be mutably referenced.
157pub trait Convert<'a, From: Convention, To: Convention>
158where
159 Self: By<'a, To> + By<'a, From>,
160{
161 /// Convert from one calling convention to another.
162 ///
163 /// Because of the generic parameters on the trait, this often requires rather explicit type
164 /// annotations.
165 ///
166 /// # Examples
167 ///
168 /// ```
169 /// use call_by::*;
170 ///
171 /// let a: u8 = <u8 as Convert<Val, Val>>::convert(1);
172 /// let b: u8 = <u8 as Convert<Ref, Val>>::convert(&2); // implicit clone
173 /// let c: u8 = <u8 as Convert<Mut, Val>>::convert(&mut 3); // implicit clone
174 ///
175 /// let d: &u8 = <u8 as Convert<Ref, Ref>>::convert(&4);
176 /// let e: &u8 = <u8 as Convert<Mut, Ref>>::convert(&mut 5);
177 ///
178 /// let b: &mut u8 = <u8 as Convert<Mut, Mut>>::convert(&mut 6);
179 /// ```
180 #[allow(clippy::wrong_self_convention)]
181 fn convert(from: <Self as By<'a, From>>::Type) -> <Self as By<'a, To>>::Type;
182}
183
184impl<'a, T> Convert<'a, Val, Val> for T {
185 fn convert(from: T) -> T {
186 from
187 }
188}
189
190impl<'a, T: 'a + Clone> Convert<'a, Ref, Val> for T {
191 fn convert(from: &T) -> T {
192 from.clone()
193 }
194}
195
196impl<'a, T: 'a + Clone> Convert<'a, Mut, Val> for T {
197 fn convert(from: &mut T) -> T {
198 Clone::clone(from)
199 }
200}
201
202impl<'a, T: 'a> Convert<'a, Ref, Ref> for T {
203 fn convert(from: &T) -> &T {
204 from
205 }
206}
207
208impl<'a, T: 'a> Convert<'a, Mut, Ref> for T {
209 fn convert(from: &mut T) -> &T {
210 &*from
211 }
212}
213
214impl<'a, T: 'a> Convert<'a, Mut, Mut> for T {
215 fn convert(from: &mut T) -> &mut T {
216 from
217 }
218}
219
220/// The generalization of [`Into`], [`AsRef`], and [`AsMut`]: in a calling-convention polymorphic
221/// context, this trait allows you to invoke the appropriate conversion method depending on the
222/// applicable calling convention.
223///
224/// # Examples
225///
226/// ```
227/// use call_by::*;
228///
229/// fn do_something<'a, T, S, C>(input: <S as By<'a, C>>::Type)
230/// where
231/// T: By<'a, C>,
232/// S: By<'a, C> + As<'a, C, T>,
233/// C: Convention,
234/// {
235/// let t: <T as By<'a, C>>::Type = S::as_convention(input);
236/// // ... do something with `t` ...
237/// }
238/// ```
239pub trait As<'a, C: Convention, T: By<'a, C>>: By<'a, C> {
240 #[allow(clippy::wrong_self_convention)]
241 fn as_convention(this: <Self as By<'a, C>>::Type) -> <T as By<'a, C>>::Type;
242}
243
244impl<'a, T, S> As<'a, Val, T> for S
245where
246 S: Into<T>,
247{
248 fn as_convention(this: S) -> T {
249 this.into()
250 }
251}
252
253impl<'a, T: 'a, S: 'a> As<'a, Ref, T> for S
254where
255 S: AsRef<T>,
256{
257 fn as_convention(this: &S) -> &T {
258 this.as_ref()
259 }
260}
261
262impl<'a, T: 'a, S: 'a> As<'a, Mut, T> for S
263where
264 S: AsMut<T>,
265{
266 fn as_convention(this: &mut S) -> &mut T {
267 this.as_mut()
268 }
269}
270
271/// Safe, zero-cost cast from `<T as By<'a, Val>>::Type` to `T`.
272///
273/// Rust's type system does not always know that `<T as By<'a, Val>>::Type` is `T` for all `'a`.
274/// This function safely converts from the former to the latter.
275pub fn to_val<'a, T: By<'a, Val>>(by_val: T::Type) -> T {
276 let ptr = &by_val as *const <T as By<'a, Val>>::Type as *const T;
277 let val = unsafe { ptr::read(ptr) };
278 mem::forget(by_val); // prevent double-free
279 val
280}
281
282/// Safe, zero-cost cast from `T` to `<T as By<'a, Val>>::Type`.
283///
284/// Rust's type system does not always know that `T` is `<T as By<'a, Val>>::Type` for all `'a`.
285/// This function safely converts from the former to the latter.
286pub fn from_val<'a, T: By<'a, Val>>(by_val: T) -> T::Type {
287 let ptr = &by_val as *const T as *const <T as By<'a, Val>>::Type;
288 let val = unsafe { ptr::read(ptr) };
289 mem::forget(by_val); // prevent double-free
290 val
291}
292
293/// Safe, zero-cost cast from `<T as By<'a, Ref>>::Type` to `&'a T`.
294///
295/// Rust's type system does not always know that `<T as By<'a, Ref>>::Type` is `&'a T` for all `'a`.
296/// This function safely converts from the former to the latter.
297pub fn to_ref<'a, T: By<'a, Ref>>(by_ref: T::Type) -> &'a T {
298 let ptr = &by_ref as *const <T as By<'a, Ref>>::Type as *const &'a T;
299 unsafe { ptr::read(ptr) }
300}
301
302/// Safe, zero-cost cast from `&'a T` to `<T as By<'a, Ref>>::Type`.
303///
304/// Rust's type system does not always know that `&'a T` is `<T as By<'a, Ref>>::Type` for all `'a`.
305/// This function safely converts from the former to the latter.
306pub fn from_ref<'a, T: By<'a, Ref>>(by_ref: &'a T) -> T::Type {
307 let ptr = &by_ref as *const &'a T as *const <T as By<'a, Ref>>::Type;
308 unsafe { ptr::read(ptr) }
309}
310
311/// Safe, zero-cost cast from `<T as By<'a, Mut>>::Type` to `&'a mut T`.
312///
313/// Rust's type system does not always know that `<T as By<'a, Mut>>::Type` is `&'a mut T` for all
314/// `'a`. This function safely converts from the former to the latter.
315pub fn to_mut<'a, T: By<'a, Mut>>(by_mut: T::Type) -> &'a mut T {
316 let ptr = &by_mut as *const <T as By<'a, Mut>>::Type as *const &'a mut T;
317 unsafe { ptr::read(ptr) }
318}
319
320/// Safe, zero-cost cast from `&'a mut T` to `<T as By<'a, Mut>>::Type`.
321///
322/// Rust's type system does not always know that `&'a mut T` is `<T as By<'a, Mut>>::Type` for all
323/// `'a`. This function safely converts from the former to the latter.
324pub fn from_mut<'a, T: By<'a, Mut>>(by_mut: &'a mut T) -> T::Type {
325 let ptr = &by_mut as *const &'a mut T as *const <T as By<'a, Mut>>::Type;
326 unsafe { ptr::read(ptr) }
327}
328
329mod sealed {
330 use super::*;
331
332 pub trait Convention {}
333 impl Convention for Val {}
334 impl Convention for Ref {}
335 impl Convention for Mut {}
336}