Rustコトハジメ

プログラミング言語Rustに関する情報をお届けします。

なぜIntoからFromへの実装が定義されていないのか実験して調べる

先日の記事

www.rustforbeginners.com

で、Fromの実装からIntoへの実装は定義されているが、逆は定義されていないことを確かめました。

そして、その理由として、型Tについて処理を定義しているモジュールの中でimpl XXX for Tを定義するならばXXXはIntoではなくFromの方がいいからだと考察しました。しかし、ここでは「IntoとFromのどちらか1つを選択するならば」という前提に言及していません。両方定義するとどうなるのでしょうか?実験で調べてみます。

自分のクレートでIntoやFromを定義しているわけではないので、impl<S, T> From<S> for S where: Into<T>は定義出来ません。なので、すべて自分で実装する必要があります。Into2From2という名前にしましょう。

trait Into2<T> {
    fn into(self) -> T;
}

trait From2<S> {
    fn from(s: S) -> Self;
}

impl<S, T> Into2<T> for S where T: From2<S> {
    fn into(self) -> T {
        T::from(self)
    }
}

impl<S, T> From2<S> for T where S: Into2<T> {
    fn from(s: S) -> T {
        s.into()
    }
}

struct A {
    x: i32
}
impl Into2<i32> for A {
    fn into(self) -> i32 {
        self.x
    }
}

fn main() {}

これをコンパイルしようとすると、

error[E0119]: conflicting implementations of trait `Into2<i32>` for type `A`:
  --> prog.rs:24:1
   |
9  | impl<S, T> Into2<T> for S where T: From2<S> {
   | ------------------------------------------- first implementation here
...
24 | impl Into2<i32> for A {
   | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `A`

という怒られが発生します。

これは、

  1. Into2<i32> for Aを定義した
  2. From2<A> for i32が自動的に定義された
  3. Into2<i32> for Aが自動的に定義された
  4. 2つの定義が衝突した

と解釈出来ます。ここで、"first implementation"と言っていますが、汎用的な実装が"first"として採用されるという仕組みではなく、単にコード内の位置関係が問題のようで、impl Into2<i32> for Aをより上に定義すれば、以下のように逆になります。

error[E0119]: conflicting implementations of trait `Into2<i32>` for type `A`:
  --> prog.rs:18:1
   |
12 | impl Into2<i32> for A {
   | --------------------- first implementation here
...
18 | impl<S, T> Into2<T> for S where T: From2<S> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `A`