How to avoid duplicated code when implementing on owned and reference

Hi all! I have doing too much this days, a lot of questions :3

I found something... curious, I was trying to do a design using generics, the idea is have a Point, which will build a Line, the key part is that the Line should be made using Point or &mut Point.

I has been testing to do this.... but I has not been able to be satisfied with my answer, my actual one is define Point as a Trait, and make the Line using the Trait as constrain, with this I have opened the door to define a NPoint struct, and from there I can implement the trait on NPoint and &mut Point.

The issue with this solution is the high amount of code duplication, after al, both implementations of Point on NPoint will be the same code....., I tried to look on Deref and played a little but I was not able to do it works to also make a new solution.

use std::marker::PhantomData;

pub trait Num:
        num_traits::Num
        + Clone
        + num_traits::NumCast
        + PartialOrd
        + std::fmt::Debug
        + Default
        + num_traits::Float
        + TryFrom<f64, Error: std::fmt::Debug>
{
}
impl<
                T: num_traits::Num
                        + Clone
                        + num_traits::NumCast
                        + PartialOrd
                        + std::fmt::Debug
                        + Default
                        + num_traits::Float
                        + TryFrom<f64, Error: std::fmt::Debug>,
        > Num for T
{
}

trait Point<const N: usize, T: Num> {
        fn ref_coords(&self) -> &[T; N];
        fn ref_mut_coords(&mut self) -> &mut [T; N];
        fn coords(&self) -> [T; N];
}

struct NPoint<const N: usize, T: Num> {
        coords: [T; N],
}

impl<const N: usize, T: Num> Point<N, T> for &mut NPoint<N, T> {
        fn ref_coords(&self) -> &[T; N] {
                &self.coords
        }

        fn ref_mut_coords(&mut self) -> &mut [T; N] {
                &mut self.coords
        }

        fn coords(&self) -> [T; N] {
                self.coords
        }
}

impl<const N: usize, T: Num> Point<N, T> for NPoint<N, T> {
        fn ref_coords(&self) -> &[T; N] {
                &self.coords
        }

        fn ref_mut_coords(&mut self) -> &mut [T; N] {
                &mut self.coords
        }

        fn coords(&self) -> [T; N] {
                self.coords
        }
}

struct NLine<const N: usize, T: Num, P: Point<N, T>> {
        pub from: P,
        pub to: P,
        phantom: PhantomData<T>,
}

impl<const N: usize, T: Num, P: Point<N, T>> NLine<N, T, P> {
        pub fn new(from: P, to: P) -> Self {
                NLine {
                        from,
                        to,
                        phantom: PhantomData,
                }
        }
}

fn main() {
        let mut point1 = NPoint { coords: [0.0] };
        let mut point2 = NPoint { coords: [1.0] };
        let line = NLine::new(point1, point2);
}

Someone have any idea how to improve this design to avoid duplicate so much code?

Note: The idea is repeat something very similar with Line to set of Lines, the input lines could be references or owned, also Points and Lines can have more functions.

Thx!

I sometimes struggle with designing traits over generics when I think to much "from inside out" - that is, I think "What functions could a user of this trait possibly need?". In my experience, this leads to a maximally restrictive impl Trait for X experience.

Usually, you want to find a balance (thinking "outside in"), by asking "What concrete examples do I have for how the trait will be used?" This can help guide what functions you put on the trait, and whether you can split the trait for use in different contexts.

The current Point<T, N> trait you mentioned seems to me like it's really a mix of three different concepts: Deref<Target=[T; N]>, DerefMut<Target=[T; N]>, and Clone/ToOwned.

If you can better define the user(s) of your trait, it should help narrow down what functionality you really need. I expect NLine is serving as a tuple new type right now NLine<T>(T, T), so you may want to define functions on NLine and see what access they need.

This SO answer recommends using Borrow.

Hi, I have checked what I need and the examples, I need all of this, the functions must be available on the three forms.

I has not been about to found docs where I can understand better Deref and DerefMut, I have seen some examples but is like the usage is out of what a normal documentation describes.

Similar with Borrow, I tried reading and using it but I still do not understand them very well to mix on this.