A pattern for flexible factory references using Deref newtypes

โš“ Rust    ๐Ÿ“… 2025-09-15    ๐Ÿ‘ค surdeus    ๐Ÿ‘๏ธ 1      

surdeus

A pattern for flexible factory references using Deref newtypes

This post is generated via Grok because my english isnt good.

Hi everyone!

I've been tinkering with a way to handle "factory" types (like ranges) that create child items needing a back-reference to the factory. The trick is supporting different ref kindsโ€”&T, Rc<T>, etc.โ€”without separate trait impls for each.

Rust's coherence rules block direct blanket impls, so I use a thin Deref newtype wrapper. It's a twist on the classic newtype pattern, but handy for this. Thoughts? Useful? Already in a crate somewhere?

Quick Example

Here's a generic countable range. Items hold a Deref ref to the range for bounds/indexing.

use std::ops::{Add, Deref, Range, RangeFrom, Sub};
use std::marker::PhantomData;
use std::fmt::Debug;

// Wrapper for any Deref<Target = Target>.
pub struct NTDeref<Ref, Target>(pub Ref, PhantomData<fn() -> Target>);

impl<Ref, Target> NTDeref<Ref, Target> {
    pub fn new(r: Ref) -> Self { Self(r, PhantomData) }
}

pub trait ToNTDeref<Target>
where Self: Deref<Target = Target> + Sized {
    fn as_nt_deref(self) -> NTDeref<Self, Target>;
}

impl<Ref, Target> ToNTDeref<Target> for Ref
where Ref: Deref<Target = Target> + Sized {
    fn as_nt_deref(self) -> NTDeref<Self, Target> {
        NTDeref::new(self)
    }
}

// Item trait: iterate and index relative to a range.
pub trait CountableRangeItem: Sized {
    fn next(self) -> Option<Self>;
    fn index(&self) -> usize;
    fn zero_assign(&mut self); // Reset to start.
}

// Factory trait: takes self by value (for ownership).
pub trait CountableRangeForRef {
    type Item: CountableRangeItem;
    fn item_first(self) -> Self::Item;
    fn item_from_index(self, index: usize) -> Option<Self::Item>;
    fn index_len(self) -> usize;
}

pub struct CountableRangeStd<T> { range: Range<T> }

pub struct CountableRangeStdItem<T, RangeRef>
where RangeRef: Deref<Target = CountableRangeStd<T>> {
    item: T,
    ref_range: RangeRef,
}

impl<T, RangeRef> Deref for CountableRangeStdItem<T, RangeRef>
where RangeRef: Deref<Target = CountableRangeStd<T>> {
    type Target = T;
    fn deref(&self) -> &Self::Target { &self.item }
}

impl<T, RangeRef> CountableRangeItem for CountableRangeStdItem<T, RangeRef>
where
    RangeRef: Deref<Target = CountableRangeStd<T>>,
    RangeFrom<T>: Iterator<Item = T>, // a super weird way to use `Step`
    T: Copy + Ord + Sub<Output: TryInto<usize, Error: Debug>>,
{
    fn next(self) -> Option<Self>
    where
        Self: Sized,
    {
        let mut for_step = RangeFrom { start: self.item };
        let _for_step_res = for_step.next();
        let v_next = for_step.start;
        if v_next >= self.ref_range.range.end {
            None
        } else {
            Some(Self {
                item: v_next,
                ref_range: self.ref_range,
            })
        }
    }

    fn index(&self) -> usize {
        (self.item - self.ref_range.range.start).try_into().unwrap()
    }

    fn zero_assign(&mut self) {
        self.item = self.ref_range.range.start;
    }
}

impl<T, RangeRef> CountableRangeForRef for NTDeref<RangeRef,CountableRangeStd<T>>
//CountableRangeStd<T,RangeRef,ToNewRef>
where
    RangeRef: Deref<Target = CountableRangeStd<T>>,
    RangeFrom<T>: Iterator<Item = T>,
    T: Copy + Ord + Sub<Output: TryInto<usize, Error: Debug>> + Add<Output = T>,
    usize: TryInto<T, Error: Debug>,
{
    type Item = CountableRangeStdItem<T, RangeRef>;

    fn item_first(self) -> Self::Item {
        CountableRangeStdItem {
            item: self.0.range.start,
            ref_range: self.0, //.clone()
        }
    }

    fn item_from_index(self, index: usize) -> Option<Self::Item> {
        let real_idx = index.try_into().unwrap() + self.0.range.start;
        if real_idx >= self.0.range.end {
            None
        } else {
            Some(CountableRangeStdItem {
                item: real_idx,
                ref_range: self.0, //.clone()
            })
        }
    }

    fn index_len(self) -> usize {
        (self.0.range.end - self.0.range.start).try_into().unwrap()
    }
}

struct Meme;
struct MemeItem;
impl CountableRangeItem for MemeItem {
    fn next(self)->Option<Self> where Self: Sized {
        todo!()
    }

    fn index(&self)->usize {
        todo!()
    }

    fn zero_assign(&mut self) {
        todo!()
    }
}
impl<RangeRef> CountableRangeForRef for NTDeref<RangeRef,Meme>
    where 
        RangeRef: Deref<Target = CountableRangeStd<Meme>>,
{
    type Item=MemeItem;

    fn item_first(self)->Self::Item {
        todo!()
    }

    fn item_from_index(self,index:usize)->Option<Self::Item> {
        todo!()
    }

    fn index_len(self)->usize {
        todo!()
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    use std::rc::Rc;

    #[test]
    fn flexible_refs() {
        let range = CountableRangeStd { range: -2i32..2 };
        fn check<Ref: Deref<Target = CountableRangeStd<i32>>>(
            mut item: CountableRangeStdItem<i32, Ref>,
        ) {
            assert_eq!(*item, -2);
            item = item.next().unwrap(); assert_eq!(*item, -1);
            item = item.next().unwrap(); assert_eq!(*item, 0);
            item = item.next().unwrap(); assert_eq!(*item, 1);
            assert!(item.next().is_none());
        }

        // &T
        check((&range).as_nt_deref().item_first());

        let outer_item;
        {
            let rc = Rc::new(range);
            let inner_item=rc.clone().as_nt_deref().item_first();
            outer_item=rc.clone().as_nt_deref().item_first();
            check(inner_item);
        }
        // Rc<T>
        
        check(outer_item);
    }
}

Why This?

  • Wrap any ref with .as_nt_deref(), get a single blanket impl.
  • Items deref to their value (e.g., *item).
  • Works for borrows, shared ownership, etc.โ€”no dupes.
  • Drawbacks: Crate-local newtype; consumes self (ok for factories).

Could extend to iterators or builders. Any tweaks for idiomatic Rust? Cheers!

2 posts - 2 participants

Read full topic

๐Ÿท๏ธ Rust_feed