1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
//! To parameterize a function by calling convention, we can specify that it takes some `T: //! CallBy<'a, Convention>`, and say that its input is of type `<T as CallBy<'a, //! Convention>>::Type`. This is essentially a defunctionalization of Rust's reference operators. //! //! This trick can be used to permit the *implementor* of a trait to pick the calling convention for //! a value passed into (or out of) a function defined in that trait, rather than this being //! hardcoded in the trait definition. //! //! ## Examples //! //! For instance, say we wanted to define an abstraction for channels that can send values. Imagine, //! however, that some channels might need to take ownership of the values they send, while others //! might serialize values given only a reference to that value. In order to unify these two notions //! into one trait, we can parameterize over the calling convention for the input value: //! //! ```rust //! use call_by::{CallBy, CallingConvention}; //! //! trait Sender<'a, T> //! where //! T: CallBy<'a, Self::Convention>, //! { //! type Convention: CallingConvention; //! fn send(&self, value: <T as CallBy<'a, Self::Convention>>::Type); //! } //! ``` //! //! Implementers of the `Sender` trait can choose whether the associated type `Convention` should be //! `Val`, `Ref`, or `Mut`, which toggles the result of `<T as CallBy<'a, Self::Convention>>::Type` //! between `T`, `&'a T`, and `&'a mut T`, respectively. Meanwhile, callers of the `send` method on //! concretely known types don't need to specify the calling convention; the type-level function //! determines what type they need to pass as the argument to `send`, and type errors are reported //! in reference to that concrete type if it is known at the call site. //! <!-- snip --> /// There are three fundamental ways to pass a `T` as input or return a `T` as output: by [`Val`]ue, /// by shared immutable [`Ref`]erence, and by unique [`Mut`]able reference. /// /// This is a sealed trait, implemented for all three of these conventions. pub trait CallingConvention: sealed::CallingConvention {} impl CallingConvention for Val {} impl CallingConvention for Ref {} impl CallingConvention for Mut {} /// To get the type of `T` via calling convention `Convention`, write `<T as CallBy<'a, /// Convention>>::Type`. pub trait CallBy<'a, Convention: CallingConvention> { /// The type of `Self` when called by `Convention`. type Type; } /// Taking a `T` by [`Val`]ue means taking a `T` as input to or output from a function. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct Val; impl<'a, T> CallBy<'a, Val> for T { type Type = T; } /// Taking a `T` by [`Ref`]erence means taking `&'a T` as input to or output from a function. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct Ref; impl<'a, T: 'a> CallBy<'a, Ref> for T { type Type = &'a T; } /// Taking a `T` by [`Mut`]able reference means taking `&'a mut T` as input to or output from a /// function. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct Mut; impl<'a, T: 'a> CallBy<'a, Mut> for T { type Type = &'a mut T; } mod sealed { use super::*; pub trait CallingConvention {} impl CallingConvention for Val {} impl CallingConvention for Ref {} impl CallingConvention for Mut {} }