5

構造体の型パラメーターとして特性を使用する場合、借用チェッカーに問題があります。

trait Trait {}

struct FooBar;
impl Trait for FooBar{}

struct Observer<Arg> {
    action: Box<Fn(Arg) + Send>,
    // Other fields
}

impl <Arg> Observer<Arg> {
    fn new(action: Box<Fn(Arg) + Send>) -> Observer<Arg> {
        Observer{action: action}
    }

    fn execute(&self, arg: Arg) {
        (*self.action)(arg);
    }
}

fn test() {
    let mut foobar = FooBar;
    {
        let mut observer = Observer::new(Box::new(|&: param: &mut Trait| {
            // do something with param here
        }));
        observer.execute(&mut foobar);   // First borrow passes ...
        observer.execute(&mut foobar);   // This fails as "foobar" is already borrowed
    }   // The previous borrow ends here (lifetime of "observer")
}

出力は次のとおりです。

error: cannot borrow `foobar` as mutable more than once at a time
    observer.execute(&mut foobar);   // This fails as "foobar" is already borrowed
                          ^~~~~~
note: previous borrow of `foobar` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foobar` until the borrow ends
    observer.execute(&mut foobar);   // First borrow passes ...
                          ^~~~~~
note: previous borrow ends here
 {
...
 }   // The previous borrow ends here (lifetime of "observer")
 ^

それでも、次の例は機能します。

trait Trait {}

struct FooBar;
impl Trait for FooBar{}

struct Observer {
    action: Box<Fn(&mut Trait) + Send>,
    // Other fields
}

impl Observer {
    fn new(action: Box<Fn(&mut Trait) + Send>) -> Observer {
        Observer{action: action}
    }

    fn execute(&self, arg: &mut Trait) {
        (*self.action)(arg);
    }
}

fn test() {
    let mut foobar = FooBar;
    {
        let mut observer = Observer::new(Box::new(|&: param: &mut Trait| {
            // do something with param here
        }));
        observer.execute(&mut foobar);
        observer.execute(&mut foobar);
    }
}

2 番目の例は 1 番目の例のインスタンス化にすぎないため、これは私には非常に奇妙に見えますが、マクロを使用して同じことを (苦労して) 実装できる可能性があります。

クロージャーが受け取るパラメーターの型を知る必要があるため、これは非常に難しいと思いますが、この参照を保存する必要はありません...

これは借用チェッカーのバグですか? それとも私は何か間違ったことをしていますか?

rustc 1.0.0-nightly (44a287e6e 2015-01-08 17:03:40 -0800)

編集1:ユースケースを正確に

編集 2: 以下の回答で説明されているように、問題は、借用チェッカーが の有効期間をObserver<&mut Type>と同じにすることを強制する&mut Typeことです。そのため、実際には、問題は型パラメーターとしてトレイトを使用するという事実とは関係ありません (実際の構造でも同じことを行います)。
したがって、私の場合、次のObserver<Arg>ように定義することで回避策を講じることができます。

struct Observer<Arg> {
    action: Box<Fn(&mut Arg) + Send>,
}

したがって、型パラメーター Arg 自体は参照ではありませんが、これによりコードの汎用性が低くなります。誰もがより良い解決策を持っていますか?

4

1 に答える 1

3

ここでの問題は、借用チェッカーが&mut Trait参照の有効期間を全体と同じにすることを強制していることGenericStructです。これは、参照が構造体自体の型パラメーターであるためだと思います。

構造体には参照を格納するフィールドがないため (元のコードでこれを行う必要がある場合は、質問を更新してください)、型パラメーターを構造体ではなくメソッド自体に移動できます。

trait Trait{}

struct FooBar;
impl Trait for FooBar{}

struct GenericStruct;

impl GenericStruct {
    fn bar<T>(&self, _: T) {}
}

fn main() {
    let mut foobar = FooBar;

    {
        let foo = GenericStruct;
        foo.bar(&mut foobar);
        foo.bar(&mut foobar);
    }
}

これにより、への呼び出しがある限り借用が持続しfoo.bar()ます。

于 2015-01-13T21:07:13.533 に答える