3

私は2つの構造体を持っていVecます。もう一方を変更しながら、一方を反復できるようにしたい。プログラムの例を次に示します。

use std::slice;

struct S {
    a: Vec<i32>,
    b: Vec<i32>
}

impl S {
    fn a_iter<'a>(&'a self) -> slice::Iter<i32>  {
        self.a.iter()
    }
    fn a_push(&mut self, val: i32) {
        self.a.push(val);
    }
    fn b_push(&mut self, val: i32) {
        self.b.push(val);
    }
}

fn main() {
    let mut s = S { a: Vec::new(), b: Vec::new() };
    s.a_push(1);
    s.a_push(2);
    s.a_push(3);

    for a_val in s.a_iter() {
        s.b_push(a_val*2);
    }
}

しかし、次のコンパイラ エラーがあります。

$ rustc iterexample.rs 
iterexample.rs:28:9: 28:10 error: cannot borrow `s` as mutable because it is also borrowed as immutable
iterexample.rs:28         s.b_push(a_val*2);
                           ^
note: in expansion of for loop expansion
 iterexample.rs:26:5: 29:6 note: expansion site
iterexample.rs:26:18: 26:19 note: previous borrow of `s` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `s` until the borrow ends
iterexample.rs:26     for a_val in s.a_iter() {
                                   ^
note: in expansion of for loop expansion
iterexample.rs:26:5: 29:6 note: expansion site
iterexample.rs:29:6: 29:6 note: previous borrow ends here
iterexample.rs:26     for a_val in s.a_iter() {
iterexample.rs:27         println!("Looking at {}", a_val);
iterexample.rs:28         s.b_push(a_val*2);
iterexample.rs:29     }
                      ^
note: in expansion of for loop expansion
iterexample.rs:26:5: 29:6 note: expansion site
error: aborting due to previous error

コンパイラが不平を言っていることを理解しています。self私はまだそれをループしているので、for ループで借用しました。

概念的には、これを行う方法があるはずです。を変更しているだけs.bで、ループしているもの ( s.a) は変更していません。この分離を実証し、この種のプログラムをコンパイルできるようにするプログラムを作成する方法はありますか?

これは大規模なプログラムの単純化された例であるため、一般的な構造を同じに保つ必要があります (1 つが繰り返され、もう 1 つが更新されるいくつかのものを持つ構造体)。

4

4 に答える 4

4

s.a.itの代わりにを使用すると、エラーを取り除くことができますs.a_iter()。から返されたイテレータはそれ自体と同じ有効期間を持つs.a_iter()参照を保持しているため、現在のソリューションは機能しません。したがって、その参照が有効になるまで、内部の変更可能なものとして借りることはできません。具体的には、これは次の理由で発生します。sss

コンパイラは、ジェネリック パラメーターを評価するときに関数呼び出しの境界で停止します。

(あなたの場合の寿命)

ここに、非常によく似た問題の完全な説明を含む良い答えがあります: `*self` も可変として借用されるため、`self.x` を不変として借用できません

編集

S考えられる解決策は、 からイテレータを取り出すのではなく、操作を内部に持ち込むことSです。S次のようなメソッドで定義できます。

fn foreach_in_a_push_to_b<F>(&mut self, func: F) where F : Fn(&i32) -> i32 {
    for a_val in self.a.iter() {
        self.b.push(func(a_val));
    }
}

その後

s.foreach_in_a_push_to_b(|&x| x * 2);
于 2015-07-02T20:24:10.893 に答える
2

根本的な問題は、借用チェッカーがコードが安全であることを証明するのに十分な情報を持っていないことです。関数の境界で停止します。これを回避するには、コンパイラ必要な情報を取得できるように参照を分割するメソッドを記述します。

struct S {
    a: Vec<i32>,
    b: Vec<i32>
}

impl S {
    fn a_push(&mut self, val: i32) {
        self.a.push(val);
    }
    fn split_a_mut_b<'a>(&'a mut self) -> (&'a Vec<i32>, &'a mut Vec<i32>) {
        (&self.a, &mut self.b)
    }
}

fn main() {
    let mut s = S { a: Vec::new(), b: Vec::new() };
    s.a_push(1);
    s.a_push(2);
    s.a_push(3);

    let (a, b) = s.split_a_mut_b();

    for a_val in a.iter() {
        b.push(a_val*2);
    }
}

ここで重要なのは、 内split_a_mut_bで、コンパイラが 2 つの借用が重複していないことを証明できることです。元の API をより多く保持できるもう 1 つのパターンは、一時的に値を可変部分と不変部分に分解することです。

use std::slice;

#[derive(Debug)]
struct S {
    a: Vec<i32>,
    b: Vec<i32>
}

impl S {
    fn a_iter(&self) -> slice::Iter<i32>  {
        self.a.iter()
    }
    fn a_push(&mut self, val: i32) {
        self.a.push(val);
    }
    fn b_push(&mut self, val: i32) {
        self.b.push(val);
    }
    fn split_a_mut_b<F, R>(&mut self, f: F) -> R
    where F: FnOnce(&Self, &mut Self) -> R {
        use std::mem::swap;

        // Break off the mutable part(s) (or the immutable parts if there
        // are less of those).
        let mut temp = S { a: vec![], b: vec![] };
        swap(&mut self.b, &mut temp.b);

        // Call the closure.
        let result = f(self, &mut temp);

        // Glue the value back together.
        swap(&mut self.b, &mut temp.b);

        result
    }
}

fn main() {
    let mut s = S { a: Vec::new(), b: Vec::new() };
    s.a_push(1);
    s.a_push(2);
    s.a_push(3);

    s.split_a_mut_b(|imm, muta| {
        for a_val in imm.a_iter() {
            muta.b_push(a_val*2);
        }
    });

    println!("{:?}", s);
}

これはそれほど非効率的ではありません。この方法では、ヒープ アクティビティはまったく発生しません。ポインターをシャッフルしているだけです。

于 2015-07-03T02:37:43.397 に答える