通过互斥锁访问结构体中的两个向量

5

我有一个结构体,其中包含两个向量,在 Arc<Mutex<TwoArrays>> 中传递给函数。

pub struct TwoArrays {
    pub a: Vec<i32>,
    pub b: Vec<i32>,
}

fn add_arrays(mut foo: Arc<Mutex<TwoArrays>>) {
    let mut f = foo.lock().unwrap();
    //Loop A: compiles
    for i in 0..f.a.len() {
        for j in 0..f.b.len() {
            f.b[j] += f.a[i];
        }
    }
    //Loop B: does not compile
    for i in f.a.iter() {
        for j in 0..f.b.len() {
            f.b[j] += i;
        }
    }
}

当我使用迭代器制作循环,并在其中写入另一个循环(循环B)时,编译器会报错:

error[E0502]: cannot borrow `f` as mutable because it is also borrowed as immutable

循环A编译完成。

  • 为什么对f进行了不可变借用?
  • 我能否只对每个数组进行借用?也就是说,对f.b进行可变借用,对f.a进行不可变借用?
  • 为什么直接传递TwoArrays时不会出现这种情况?只有当我将其作为Arc<Mutex<TwoArrays>>传递时才会发生。

你可以通过查看错误来回答你的第一个问题。 - hellow
usize 实现了 copy,因此当您调用 f.a.len() 时,会得到一个新的 usize。借用仅在调用长度函数的持续时间内有效。但是,当您创建不可变迭代器(应该使用 iter_mut)时,您正在借用 f 以在迭代器中使用。当它尝试再次借用 f 来获取 b 的长度时,发现它已经被借用了,就会出现错误。 - Locke
在循环A中,您只获取长度值并使用该值进行循环。但是使用iter()函数,您可以迭代向量的迭代器。这是主要区别。 - Akiner Alkan
Vec 不是一个数组。 - Shepmaster
2个回答

4
当您取消包装LockResult时,您会得到一个MutexGuard,而不是直接的TwoArrays。您可以将其用作TwoArrays,因为它实现了DerefDerefMut
当您尝试编写2个循环时,您尝试同时使用derefderef_mut:这是不可能的。
pub struct TwoArrays {
    pub a: Vec<i32>,
    pub b: Vec<i32>,
}

fn add_arrays(mut foo: Arc<Mutex<TwoArrays>>) {
    let mut f = foo.lock().unwrap();

    //Loop B: does not compile
    for i in f.a.iter() {
          //  ^~~~~~~~~~~~~~~~~~~ Implicit call to `deref` here.
        for j in 0..f.b.len() {
          //         ^~~~~~~~~~~~ Another implicit call to `deref` here.
            f.b[j] += i;
          // ^~~~~~~~~~~~~~~~~~~~ Implicit call to `deref_mut` here.
        }
    }
}

如果您在循环之前只使用一次deref_mut,那么一切都可以正常工作:

use std::{sync::{Arc, Mutex}, ops::DerefMut};

pub struct TwoArrays {
    pub a: Vec<i32>,
    pub b: Vec<i32>,
}

fn add_arrays(foo: &mut Arc<Mutex<TwoArrays>>) {
    let mut mutex_guard = foo.lock().unwrap();
    let real_two_arrays = mutex_guard.deref_mut();

    for i in &mut real_two_arrays.a {
        for j in &real_two_arrays.b {
            *i += *j;
        }
    }
}

1
您可以像下面这样访问结构体中的两个向量:
use std::mem;

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

fn add_arrays(mut foo: TwoArrays) {
    let a = foo.a.clone();
    let mut b = foo.b.clone();

    for i in a.iter() {
        let mut index = 0;
        for _j in b.iter_mut() {
            let mut new_value = i.clone() + foo.b[index as usize].clone();
            mem::swap(&mut foo.b[index as usize], &mut new_value);
            index = index + 1;
        }
    }

    println!("Arrays A: {:?}", &foo.a);
    println!("Arrays A: {:?}", &foo.b);
}

fn main() {
    let a = vec![1i32, 2i32, 3i32];
    let b = vec![4i32, 5i32, 6i32];
    let two_arrays = TwoArrays { a, b };
    // let foo = Arc::new(Mutex::new(two_arrays));

    add_arrays(two_arrays);
}

游乐场

为什么对f进行不可变借用?

因为您尝试使用iter()而不是iter_mut()迭代它。

当我直接传递TwoArrays时为什么不会发生这种情况?只有在我将其作为Arc<Mutex<TwoArrays>>传递时才会发生。

您可以使用示例代码将其作为裸结构体传递,而无需Arc<Mutex<>>

如果您坚持使用Arc<Mutex<>>以原子引用的方式传递相同的对象,可以将函数签名更改为以下内容:

fn add_arrays(mut foo: Arc<Mutex<TwoArrays>>)

您需要调用lock()方法并通过以下方式从Arc获取该引用:

let foo = foo.lock().unwrap();

使用弧线的游乐场


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接