morphix/helper/mod.rs
1//! Helper utilities for internal implementation details.
2//!
3//! This module contains traits and types that support morphix's internal machinery. These are
4//! implementation details and should not be used directly in most cases.
5//!
6//! ## Contents
7//!
8//! - [`Assignable`] - Enables assignment operations on observers via autoref-based specialization
9//! - [`Unsigned`], [`Zero`], [`Succ`] - Type-level natural numbers for compile-time depth tracking
10//! - [`AsDeref`], [`AsDerefMut`] - Inductive recursive dereferencing
11//! - [`AsDerefCoinductive`], [`AsDerefMutCoinductive`] - Coinductive recursive dereferencing
12//!
13//! ## Stability
14//!
15//! Items in this module are considered internal implementation details and may change between minor
16//! versions without notice. Use at your own risk.
17
18pub mod deref;
19pub mod unsigned;
20
21pub use deref::{AsDeref, AsDerefCoinductive, AsDerefMut, AsDerefMutCoinductive};
22pub use unsigned::{Succ, Unsigned, Zero};
23
24/// A trait enabling assignment to observers using autoref-based specialization.
25///
26/// ## Background
27///
28/// Rust doesn't allow overloading the assignment operator (`=`). This creates a problem for
29/// observers: when you write `observer.field = value`, you want to assign to the observed field,
30/// not replace the observer itself. While [`DerefMut`](std::ops::DerefMut) handles most operations,
31/// it doesn't work for direct assignment due to Rust's assignment semantics.
32///
33/// ## Autoref-based Specialization
34///
35/// `Assignable` uses a technique called autoref-based specialization to solve this:
36///
37/// 1. The trait provides a method [`__assign`](Assignable::__assign) with a default implementation
38/// 2. We implement it for `&mut T` (all mutable references)
39/// 3. We also implement it for each [`Observer`](crate::observe::Observer) type
40/// 4. The [`observe!`](crate::observe!) macro automatically rewrites assignment expressions:
41///
42/// ```
43/// # use morphix::helper::Assignable;
44/// # let mut value = 0i32;
45/// // User writes:
46/// value = 42;
47///
48/// // Macro transforms to:
49/// (&mut value).__assign(42);
50/// ```
51///
52/// This transformation ensures assignments work correctly for both regular fields and observed
53/// fields without requiring different syntax:
54/// - For normal values: calls `&mut T` impl, effectively becoming `*(&mut left) = right`.
55/// - For observers: calls the observer's impl, properly dereferencing through the observer.
56///
57/// This creates a form of specialization without requiring the unstable specialization feature.
58///
59/// ## Implementation Notes
60///
61/// 1. **Every type implementing [`Observer`](crate::observe::Observer) should manually implement
62/// `Assignable`**. Without this implementation, assignments in the [`observe!`](crate::observe!)
63/// macro may not work as expected, potentially causing compilation errors or incorrect behavior.
64/// We cannot provide a blanket implementation `impl<T: Observer> Assignable for T` because it
65/// would conflict with the `impl<T> Assignable for &mut T` implementation.
66///
67/// 2. **Do not implement `Assignable` for types other than `&mut T` and
68/// [`Observer`](crate::observe::Observer) types**. Implementing `Assignable` for other
69/// [`DerefMut`](std::ops::DerefMut) types (like [`Box`], [`MutexGuard`](std::sync::MutexGuard),
70/// etc.) may cause unexpected behavior in the [`observe!`](crate::observe!) macro, as it would
71/// interfere with the autoref-based specialization mechanism.
72///
73/// ## Example
74///
75/// Implement `Assignable` for a custom observer type:
76///
77/// ```
78/// # use morphix::helper::{Assignable, Succ, Zero};
79/// # struct MyStruct<'i, T>(&'i mut T);
80/// # impl<'i, T> std::ops::Deref for MyStruct<'i, T> {
81/// # type Target = T;
82/// # fn deref(&self) -> &Self::Target { &self.0 }
83/// # }
84/// # impl<'i, T> std::ops::DerefMut for MyStruct<'i, T> {
85/// # fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
86/// # }
87/// impl<'i, T> Assignable for MyStruct<'i, T> {
88/// // Uses the default implementation which calls `DerefMut::deref_mut`
89/// type Depth = Zero;
90/// }
91/// ```
92pub trait Assignable: AsDerefMutCoinductive<Succ<Self::Depth>, Target: Sized> {
93 type Depth: Unsigned;
94
95 /// Internal method for assignment operations.
96 ///
97 /// This method is automatically used by the [`observe!`](crate::observe) macro.
98 #[doc(hidden)]
99 fn __assign(&mut self, value: Self::Target) {
100 *self.as_deref_mut_coinductive() = value;
101 }
102}
103
104impl<T> Assignable for &mut T {
105 type Depth = Zero;
106}
107
108// The impl below will conflict with `&mut T`, so we have to impl `Assignable` for every single
109// `Observer` types.
110// impl<'i, T: crate::observe::Observer<'i, InnerDepth = Zero>> Assignable for T {
111// type Depth = T::OuterDepth;
112// }