ライフタイム回りに関する一考察
ライフタイム回りに関する一考察
何を扱い何を扱わないのか
ここでは、Lifetime annotationの意味をすでに理解している前提で話を進める。
今回ここで扱うのは、Lifetime annotationをimpl、trait、structに適用させる意味を検証していく。
structに対するLifetime annotation
structに対するLifetime annotationは比較的理解しやすいだろう。structのメンバフィールドに何らかの参照型を持たせようとした場合、
その参照型を修飾するLifetime annotationが必要となる。
そのような場合、Lifetime annotationは必須となる。以下に例を示す。
また、以下の例ではi32を直接使った場合、CopyTraitの作用により不用意にコピーが行われてしまい意図とは異なる挙動を取りかねないので敢えてNew typeを作成することにした。
//元になる内容
struct Content(i32);
//名前付きタプルの場合
struct RefEnvelope<'a>(&'a Content);
この例では、RefEnvelope
がContent
の参照フィールドを持っているので、そのライフタイムを修飾するためにLifetime annotationが必須となる。
implに対するLifetime annotation
次にLifetime annotationをimplに適用する例を考えてみる。以下の例のような場合、implそのものにLifetime annotationを付けることが必須となる。
//Example of impl<'a>
impl<'a> From<&'a Content> for RefEnvelope<'a> {
fn from(content: &'a Content) -> Self {
RefEnvelope(content)
}
}
このような場合、Self
はLifetime annotationで修飾することが出来ないので、先に修飾する必要がある。その場合、implの定義部で修飾することが必要になることから、上記のような形態を取る。
また、注意すべき点として、匿名ライフタイムが有る。以下のように記載した場合、とりあえずコンパイルは通せる
impl From<&'_ Content> for RefEnvelope<'_> {
fn from(content: &'_ Content) -> Self {
todo!()
}
}
しかし、実際に実装するとコンパイルエラーになる
impl From<&'_ Content> for RefEnvelope<'_> {
fn from(content: &'_ Content) -> Self {
RefEnvelope(content)
}
}
error: lifetime may not live long enough
--> playground\src\main.rs:9:3
|
8 | fn from(content: &'_ Content) -> Self {
| - ---- return type is RefEnvelope<'2>
| |
| let's call the lifetime of this reference `'1`
9 | RefEnvelope(content)
| ^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lif`'1`
なぜエラーになるかというと、implで使った匿名ライフタイムと、fnで使った匿名ライフタイムは別個のライフタイムを示しているコトになるからである。
traitへのLifetime annotation
それでは最後にtraitへのLifetime annotationを検討していく。
必須になるのは概ね以下のようなシナリオかと思う。
trait MyZip<T,U>{
fn zip(a:T,b:U)->Zipping<T,U>;
}
trait RefMyZip<'a,T>:MyZip<T,RefEnvelope<'a>>{
}
何かしらのtraitからderived traitをこさえたとき、型引数の一部を代入してなおかつその代入した対象にLifetime annotationが必要な場合、必要になるかと思う。
Discussion