Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I implement Into for &MyType and &mut MyType without code duplication?

Tags:

rust

Here is a toy example:

#[derive(Debug)]
struct Int {
    v: i32,
}

#[derive(Debug)]
struct Double {
    v: f64,
}

impl Into<Double> for Int {
    fn into(self) -> Double {
        Double {
            v: f64::from(self.v),
        }
    }
}

This works, but really I want to implement Into<Double> for &Int and &mut Int. This doesn't work:

impl<T> Into<Double> for T
where
    T: AsRef<Int>,
{
    fn into(self) -> Double {
        Double {
            v: f64::from(self.as_ref().v),
        }
    }
}

because trait Into is not defined in my crate:

error[E0119]: conflicting implementations of trait `std::convert::Into<Double>`:
  --> src/main.rs:19:1
   |
19 | / impl<T> Into<Double> for T
20 | | where
21 | |     T: AsRef<Int>,
22 | | {
...  |
27 | |     }
28 | | }
   | |_^
   |
   = note: conflicting implementation in crate `core`:
           - impl<T, U> std::convert::Into<U> for T
             where U: std::convert::From<T>;

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`); only traits defined in the current crate can be implemented for a type parameter
  --> src/main.rs:19:1
   |
19 | / impl<T> Into<Double> for T
20 | | where
21 | |     T: AsRef<Int>,
22 | | {
...  |
27 | |     }
28 | | }
   | |_^

How should I implement Into<Double> for &Int and &mut Int, without code duplication like:

impl<'a> Into<Double> for &'a Int {
impl<'a> Into<Double> for &'a mut Int {
like image 971
user1244932 Avatar asked Mar 06 '23 08:03

user1244932


1 Answers

You can do what you want, by implementing From instead of its friend Into:

impl<T> From<T> for Double
where
    T: AsRef<Int>,
{
    fn from(i: T) -> Self {
        Double {
            v: f64::from(i.as_ref().v),
        }
    }
}

This way we avoid implementing a trait for a generic parameter (the for T part) which is disallowed by orphan rules. From and Into are linked together with this awesome blanket impl:

impl<T, U> Into<U> for T 
where
    U: From<T>, 

However, AsRef is not the trait you are looking for here (I think). Borrow might be more fitting in your situation:

impl<T> From<T> for Double
where
    T: Borrow<Int>,
{
    fn from(i: T) -> Self {
        Double {
            v: f64::from(i.borrow().v),
        }
    }
}

This way, the conversion is possible for Int, &Int and &mut Int:

fn foo<T: Into<Double>>(_: T) {}

foo(Int { v: 3 });
foo(&Int { v: 3 });
foo(&mut Int { v: 3 });

See also:

  • When should I implement std::convert::From vs std::convert::Into?
like image 123
Lukas Kalbertodt Avatar answered Apr 09 '23 02:04

Lukas Kalbertodt