imply_hack/lib.rs
1//! Add implied bounds to your traits by adding [`Imply`] as a super trait:
2//!
3//! ```rust
4//! trait Bound {}
5//!
6//! trait MyTrait<T>: Imply<T, Is: Bound> {} // Implies T: Bound
7//! ```
8//!
9//! Works with Rust 1.79+.
10//!
11//! For more information, see [Why](#the-problem) and [How](#how-it-works).
12//!
13//! ## The problem
14//!
15//! If you're the type of person to get lost deep into generic code, you might have run into something
16//! like this:
17//!
18//! ```rust
19//! trait MyTrait<T> {
20//! fn do_the_thing(value: &T);
21//! }
22//!
23//! struct Foo;
24//!
25//! struct MyFooUser;
26//!
27//! impl MyFooUser {
28//! fn use_value<T>(&self, value: &T)
29//! where
30//! Foo: MyTrait<T>
31//! {
32//! Foo.do_the_thing(value)
33//! }
34//! }
35//!
36//! fn run<T>(value: &T, user: MyFooUser)
37//! where
38//! Foo: MyTrait<T>,
39//! {
40//! MyFooUser.use_value(&value);
41//!
42//! Foo.do_the_thing(&value); // Do it again!
43//! }
44//! ```
45//!
46//! Now, this is all well and good. But suppose we now want to make `run` generic over any `FooUser`.
47//!
48//! ```rust
49//! trait FooUser<T>
50//! {
51//! fn use_value(&self, value: &T);
52//! }
53//!
54//! impl<T> FooUser<T> for MyFooUser
55//! where
56//! Foo: MyTrait<T>
57//! {
58//! fn use_value(&self, value: &T) { /* ... */ }
59//! }
60//! ```
61//!
62//! Now, suppose that `FooUser<T>` only really makes sense when `Foo: MyTrait<T>` and, notice that we
63//! use `Foo: MyTrait<T>` both in `run` and in the implementation of `FooUser<T>`.
64//!
65//! We're violating one of the most important rules of software development: Dont Repeat Yourself!
66//!
67//! > Note: It might not seem that big of a deal in this example, but imagine that what I'm representing
68//! > here as a simple bound is, in fact, decidedly **not** simple. And that `run` is not just a
69//! > function, but a trait implementation. One of many.
70//!
71//! ```rust
72//! fn run<T, U>(value: T, user: U)
73//! where
74//! U: FooUser<T>,
75//! Foo: MyTrait<T>, // We really want to get rid of this.
76//! {
77//! user.use_value(&value)
78//! Foo.do_the_thing(&value);
79//! }
80//! ```
81//!
82//! If you've run into similar situations before, you might be tempted to do:
83//!
84//! ```rust
85//! trait FooUser<T>
86//! where
87//! Foo: MyTrait<T>
88//! { /* ... */ }
89//!
90//! fn run<T, U>(value: T, user: U)
91//! where
92//! U: FooUser<T>,
93//! { /* ... */ }
94//! ```
95//! But this does not quite work...
96//!
97//! ```text
98//! error[E0277]: the trait bound `Foo: MyTrait<T>` is not satisfied
99//! --> src/lib.rs:31:8
100//! |
101//! 31 | U: FooUser<T>,
102//! | ^^^^^^^^^^ the trait `MyTrait<T>` is not implemented for `Foo`
103//! |
104//! note: required by a bound in `FooUser`
105//! --> src/lib.rs:26:10
106//! |
107//! 24 | trait FooUser<T>
108//! | ------- required by a bound in this trait
109//! 25 | where
110//! 26 | Foo: MyTrait<T>
111//! | ^^^^^^^^^^ required by this bound in `FooUser`
112//! ```
113//!
114//! Congratulations! You just stumbled into [RFC 2089: Extended Implied
115//! bounds](https://rust-lang.github.io/rfcs/2089-implied-bounds.html)
116//!
117//! ## Possible solutions
118//!
119//! 1. Wait for stabilization (See you in 10 years).
120//! 2. Grab a copy of rustc, zulip and get coding!
121//! 3. Bite the bullet, and start typing bounds.
122//! 4. Rework the entire architecture.
123//!
124//! None of these is particularly appealing if you don't want to start a very big side quest.
125//!
126//! Or...
127//!
128//! 5. Use this crate.
129//!
130//! ## How it works
131//!
132//! Suppose we want `MyTrait` to imply `T: Bound`.
133//!
134//! Rust 1.79 [stabilized](https://github.com/rust-lang/rust/pull/122055/#issue-2170532454) implied
135//! bounds on super traits and, notably, associated bounds of super traits.
136//!
137//! We can use this by creating a supertrait for `MyTrait`, and then constraining an associated type on
138//! that super trait (which we set equal to `T`), such that it satisfies `Bound`. This looks like this:
139//!
140//! ```rust
141//! trait Imply {
142//! type Is;
143//! }
144//!
145//! trait MyTrait<T>
146//! where
147//! Self: Imply<Is: Bound>,
148//! Self: Imply<Is = T>,
149//! {}
150//! ```
151//!
152//! This is still a bit annoying to use. Refining the design a bit we get:
153//!
154//! ```rust
155//! trait Imply<T>: ImplyInner<T, Is = T> {}
156//!
157//! trait ImplyInner<T> {
158//! type Is;
159//! }
160//!
161//! trait MyTrait<T>: Imply<T, Is: Bound> {}
162//! ```
163//!
164//! Then, add a few blanket impls and we have `imply_hack`!
165
166/// Creates an implied bound when applied as a supertrait.
167///
168/// ```rust
169/// trait MyTrait<T>: Imply<T, Is: Bound> {} // Implies T: Bound
170/// ```
171pub trait Imply<T>: sealed::ImplyInner<T, Is = T> {}
172
173impl<T, U> Imply<T> for U {}
174
175mod sealed {
176 pub trait ImplyInner<T> {
177 type Is;
178 }
179
180 impl<T, U> ImplyInner<T> for U {
181 type Is = T;
182 }
183}