7

次のコードで何が問題なのか誰にもわかりますか? コンパイラはライフタイムについて不平を言っていますが、エラーメッセージはまったく意味がありません。考えられることはすべて試しましたが、何も役に立たないようです。

use std::borrow::BorrowMut;

trait Trait<'a> {
    fn accept(&mut self, &'a u8);
}

struct Impl<'a>{
    myref: Option<&'a u8>,
}
impl<'a> Trait<'a> for Impl<'a> {
    fn accept(&mut self, inp: &'a u8) { self.myref = Some(inp); }
}

fn new<'a>() -> Box<Trait<'a> + 'a> {
    Box::new(Impl{myref: None})
}

fn user<'a>(obj: &mut Trait<'a>) {}

fn parent<'a>(x: &'a u8) {
    let mut pool = new();
    user(pool.borrow_mut());
}

コンパイルエラーは

error: `pool` does not live long enough
  --> src/wtf.rs:22:10
   |
22 |     user(pool.borrow_mut());
   |          ^^^^ does not live long enough
23 | }
   | - borrowed value dropped before borrower
   |
   = note: values in a scope are dropped in the opposite order they are created

これはまったく意味がありません。借り手はどのように長生きしていますか?借りた値すら使っていない!

4

3 に答える 3

10

わかりました、これ理にかなっていますが、生涯省略のために見にくいです。したがって、すべてのライフタイムが明示的に書き出され、無関係な詳細が選別されたコードは次のとおりです。

use std::borrow::BorrowMut;

trait Trait<'a> {}

struct Impl<'a> {
    myref: Option<&'a u8>,
}

impl<'a> Trait<'a> for Impl<'a> {}

fn new<'a>() -> Box<Trait<'a> + 'a> {
    Box::new(Impl { myref: None })
}

fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'b)) {}

fn parent() {
/* 'i: */   let mut pool/*: Box<Trait<'x> + 'x>*/ = new();
/* 'j: */   let pool_ref/*: &'i mut Box<Trait<'x> + 'x>*/ = &mut pool;
            /* BorrowMut<T>::borrow_mut<'d>(&'d mut Self) -> &'d mut T */
/* 'k: */   let pool_borrow/*: &'i mut (Trait<'x> + 'x)*/ = Box::borrow_mut(pool_ref);
            user(pool_borrow);
}

ここで、 の最後の行の観点から、parentの定義を読んでuserの寿命を代入するだけで、次の等価性を得ることができparentます。

  • 'a='x
  • 'b='i
  • 'b='x

さらに、これにより、次のことが結論付けられます。

  • 'x='i

これが問題です。を定義した方法により、借用の有効期間(借用元の保管場所の有効期間に等しい) が、使用されている有効期間と同じでなければならないuser状況に身を置くことになります。に収納されているもの。pool_refpool'xpool

それは、存在する前に自分自身へのポインターを持つことができるのと少し似てBoxいますが、これは意味がありません。

いずれにせよ、修正は簡単です。user実際に正しいタイプに変更します。

fn user<'a, 'b>(obj: &'b mut (Trait<'a> + 'a)) {}

これは、 によって生成されるタイプと一致しnewます。または、使用しないでくださいborrow_mut:

user(&mut *pool)

これは「再借用」であるため機能します。呼び出しborrow_mutは、ライフタイムを多かれ少なかれ直接的に変換しますが、再借用により、コンパイラは借用をより短いライフタイムに絞り込むことができます。別の言い方をすれば、明示的に呼び出すとborrow_mut、コンパイラーはライフタイムを「ファッジ」してすべてのライフタイムを整列させるのに十分な自由を与えられません

余談ですが:

借りた値すら使っていない!

無関係。 Rust は型チェックとライフタイム チェックを完全にローカルで行います。別の関数の本体を調べて、それ何をしているかを確認することはありません。インターフェースだけに行きます。コンパイラは、別の関数で何をしているのかをチェックしたり、気にしたりしません。

于 2016-09-06T03:30:02.070 に答える
4

エラーメッセージには他にもあることに注意してください。

error: `pool` does not live long enough
  --> src/main.rs:25:10
   |>
25 |>     user(pool.borrow_mut());
   |>          ^^^^
note: reference must be valid for the block at 23:25...
  --> src/main.rs:23:26
   |>
23 |> fn parent<'a>(x: &'a u8) {
   |>                          ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 24:25
  --> src/main.rs:24:26
   |>
24 |>     let mut pool = new();
   |>                          ^

見てみましょうuser

fn user<'a>(obj: &mut Trait<'a>) {}

これは、lifetime でパラメータ化された trait オブジェクトへの変更可能な参照 (無名のlifetime を持つ) を受け入れることを示しています'a

に目を向けると、この方法は非常に疑わしいnewと言えます。

fn new<'a>() -> Box<Trait<'a> + 'a> {
    Box::new(Impl { myref: None })
}

これは、呼び出し元が指定した有効期間を持つボックス化された特性オブジェクトを返すことを示しています。それは基本的に意味がありません

とはいえ、コードが を使用する理由がよくわかりませんborrow_mut。私はそれをもっと直接的に書いたでしょう:

user(&mut *pool);

これは を逆参照してBox<Trait>を取得しTrait、変更可能な参照を取得して、&mut Traitコンパイルします。

BorrowMut現在、動作が異なる理由を説明できません。

于 2016-09-06T01:48:35.290 に答える