LoginSignup
7
2

More than 1 year has passed since last update.

[Rust] Lifetime of associated types パターンについて

Last updated at Posted at 2022-02-03

これは何?

Rustを使っていると、参照を取得し参照を返す関数を生やしたくなることは多いです。このような目的で存在するAsRef traitの定義は以下のようになっています。

pub trait AsRef<T> 
where
    T: ?Sized, 
{
    fn as_ref(&self) -> &T;
}

ここで、as_ref()関数は&'a Selfをとり&'a Tを返す関数です。

ここで、戻り値の型を一般化し&'a TではなくS<'a, T>を返したいことがよくあります。例えばOption::as_refは以下のようになっています。

pub const fn as_ref(&self) -> Option<&T> {
    match *self {
        Some(ref x) => Some(x),
        None => None,
    }
}

このas_ref()関数はOption型に直接実装された関数です。すなわちOption型はAsRef trait を実装していません。なぜならば、シグニチャが明らかに異なるからです。

  • AsRef::as_ref の場合、&'a Self から &'a T
  • Option::as_refの場合、&'a Option<T> から Option<&'a T>

では、Option::as_refのような、ライフタイムパラメータ'aを持つような任意の型を返す関数を含むtraitを定義する方法はあるのでしょうか。

Generic Associated Types を使う方法

このような問題を扱う方法として、generic associated types(GAT)を使う方法があります。

#![feature(generic_associated_types)]

trait GetItemRef {
    type Item<'a>
    where
        Self: 'a;

    fn get_item_ref<'a>(&'a self) -> Self::Item<'a>;
}

struct S<T>(T);
struct S2<'a, T>(&'a T);

impl<T> GetItemRef for S<T> {
    type Item<'a>
    where
        Self: 'a,
    = S2<'a, T>;
    fn get_item_ref<'a>(&'a self) -> Self::Item<'a> {
        S2(&self.0)
    }
}

しかし、unstable featureを使うためnightly版のコンパイルでしかコンパイルできないという問題があります。

HKT traitを用いる方法

Rustのtrait機能を使うことにより、高階型を(制限はあるが)使えることが知られています。これを用いると実装は以下のようになります。

trait HKT<A> {
    type MA;
}
impl<'a, B, T: 'a> HKT<T> for &'a B {
    type MA = S2<'a, T>;
}

trait GetItemRef<Item>: HKT<Item> {
    fn get_item_ref(self) -> Self::MA;
}

struct S<T>(T);
struct S2<'a, T>(&'a T);

impl<'a, T> GetItemRef<T> for &'a S<T> {
    fn get_item_ref(self) -> Self::MA {
        S2(&self.0)
    }
}
7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2