Help wanted for removing code duplicate [solved]

I need some help for removing a lot of duplicates in my code (but I'm afraid I can't).

Let's assume we have a struct Bar who owns a value of type T. You can either read of it, if it is an Input-Bar, write to it if it is an Output-Bar or do both operations, if it is an InOut-Bar.

On top of that, I want to be able to get the opposite of that, in case of an Output-Bar, I want to get an Input-bar and vice versa. Inout is the opposite to itself.

Below is my approach, I like it, but is very verbose.
There is no way, that a user can implement Direction and Input, Output and InOut are the only possible structs for Direction.

use std::marker::PhantomData;

mod private {
    pub trait Sealed {}
    impl Sealed for super::Input {}
    impl Sealed for super::Output {}
    impl Sealed for super::InOut {}
}

pub trait Direction: private::Sealed {}

pub struct Input;
pub struct Output;
pub struct InOut;

impl Direction for Input {}
impl Direction for Output {}
impl Direction for InOut {}

pub struct Bar<T, D: Direction> {
    value: T,
    _marker: PhantomData<D>,
}

impl<T: Default, D: Direction> Bar<T, D> {
    pub fn new() -> Bar<T, D> {
        Bar {
            value: T::default(),
            _marker: PhantomData,
        }
    }
}

impl<T> Bar<T, Input> {
    pub fn create_opposite(self) -> Bar<T, Output> {
        Bar {
            value: self.value,
            _marker: PhantomData,
        }
    }
}

impl<T> Bar<T, Output> {
    pub fn create_opposite(self) -> Bar<T, Input> {
        Bar {
            value: self.value,
            _marker: PhantomData,
        }
    }
}

impl<T> Bar<T, InOut> {
    pub fn create_opposite(self) -> Bar<T, InOut> {
        self
    }
}

impl<T> Bar<T, Input> {
    pub fn read(&self) -> &T {
        &self.value
    }
}

impl<T> Bar<T, Output> {
    pub fn set(&mut self, value: T) {
        self.value = value
    }
}

impl<T> Bar<T, InOut> {
    pub fn read(&self) -> &T {
        &self.value
    }

    pub fn set(&mut self, value: T) {
        self.value = value
    }
}

(Playground)

You can make creating the opposite a bit more compact by using an associated type: playground

1 Like

Ahh, yes, of course. I tried to use const fns but they didn't work. I even implemented a function based on an associated type, but did not consider to use a AT in the first place. Thanks!

With a little bit of added complexity to the type system, you can make @vitalyd's solution to be extensible like this. This way also allows you to implement traits like Deref and DerefMut without duplicating code! (this can be seen at the bottom of the playground link)

1 Like

@RustyYato you forgot:

pub type Input = Dir<Read, Off>;
pub type Output = Dir<Off, Write>;
pub type InOut = Dir<Read, Write>;

:laughing:

That is very cool, I really love it and I think I will use it.
Deref is very neat, but I won't/can't use it, because my inner values are Arcs, but nevertheless, amazing work, thanks! :heart:

2 Likes

@RustyYato your example can be simplified, if you don't store D as a member, but only PhantomData.

You can omit the Default impl for Direction, the new and opp function as well.
I don't see any difference except simplicity, do you? What had you in mind while implemting those?

Oh, yeah, that works. I guess I over-engineered it a bit! :slight_smile:

1 Like