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}