ライフタイムシステムの限界

次のコードを見てみましょう。

struct Foo;

impl Foo {
    fn mutate_and_share(&mut self) -> &Self { &*self }
    fn share(&self) {}
}

fn main() {
    let mut foo = Foo;
    let loan = foo.mutate_and_share();
    foo.share();
}

このコードはコンパイルを通ると思うかもしれません。 mutate_and_share は、foo を一時的に変更可能に借り入れますが、 共有リファレンスを返します。 そうすると、foo は変更可能には借りられていないので、 foo.share() は成功すると思うでしょう。

ところが、このコードをコンパイルすると・・・。

<anon>:11:5: 11:8 error: cannot borrow `foo` as immutable because it is also borrowed as mutable
<anon>:11     foo.share();
              ^~~
<anon>:10:16: 10:19 note: previous borrow of `foo` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foo` until the borrow ends
<anon>:10     let loan = foo.mutate_and_share();
                         ^~~
<anon>:12:2: 12:2 note: previous borrow ends here
<anon>:8 fn main() {
<anon>:9     let mut foo = Foo;
<anon>:10     let loan = foo.mutate_and_share();
<anon>:11     foo.share();
<anon>:12 }
          ^

何が起こったのでしょう? 前の節の 2 つ目のサンプルと全く同じ推論を行ったのです。 このコードを脱糖すると、次のようになります。

struct Foo;

impl Foo {
    fn mutate_and_share<'a>(&'a mut self) -> &'a Self { &'a *self }
    fn share<'a>(&'a self) {}
}

fn main() {
    'b: {
        let mut foo: Foo = Foo;
        'c: {
            let loan: &'c Foo = Foo::mutate_and_share::<'c>(&'c mut foo);
            'd: {
                Foo::share::<'d>(&'d foo);
            }
        }
    }
}

loan のライフタイムと mutate_and_share のシグネチャとのため、 &mut foo のライフタイムは 'c に延長されなくてはなりません。 そして、share を呼ぼうとするとき、&'c mut foo の別名を取ろうとすると認識され、大失敗に終わるのです。

このプログラムは、私たちにとって重要なリファレンスの意味的には全く正しいのですが、 ライフタイムシステムはこのプログラムを処理するには粗すぎるのです。

TODO: その他のよくある問題は? 主に SEME 領域とか?