これは何?
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)
}
}