I'm trying to translate a C++ program that uses a Bank object, with methods that change an account's balance and uses mutexes so this can happen in parallel. In this program, each account had a lock I needed to access before I can modify the data. In C++, the Bank object is global, with many locks to protect the data. In Rust, I created a Struct Bank, where I attempted to use Arc<Mutex<i32>> and Mutex<i32>, to have an issue with moving the data to the thread, because it doesn't implement Copy. I'm fully aware I can just put Arc<Mutex<Bank>>, but in order to make it more similar to the C++ code, I want to try to just protect specific data. Here's some code:
Bank.rs
use std::sync::{ Arc, Mutex };
#[derive(Debug)]
pub struct Account {
account_id: i32,
balance: Arc<Mutex<i64>>,
}
#[derive(Debug)]
pub struct Bank {
num: Arc<Mutex<i32>>,
num_succ: Arc<Mutex<i32>>,
num_fail: Arc<Mutex<i32>>,
accounts: Vec<Account>,
}
pub fn init(n: i32) -> Bank {
let mut accounts: Vec<Account> = Vec::new();
for i in 0..n {
accounts.push(Account {
account_id: i,
balance: Arc::new(Mutex::new(0)),
});
}
Bank {
num: Arc::new(Mutex::new(0)),
num_succ: Arc::new(Mutex::new(0)),
num_fail: Arc::new(Mutex::new(0)),
accounts: accounts,
}
}
impl Bank {
pub fn print_account(&self) {
for i in &self.accounts {
let balance_lock = i.balance.lock().unwrap();
println!("ID# {} | {}", i.account_id, balance_lock);
}
println!(
"Success: {} Fails: {}",
self.num_succ.lock().unwrap(),
self.num_fail.lock().unwrap(),
);
}
pub fn record_succ(&self, message: String) {
let mut succ_lock = self.num_succ.lock().unwrap();
println!("{message}");
*succ_lock+=1;
}
pub fn record_fail(&self, message: &str) {
let mut fail_lock = self.num_fail.lock().unwrap();
println!("{message}");
*fail_lock+=1;
}
pub fn deposit(&self, worker_id: i32, ledger_id: i32, account_id: i32, amount: i32) {
let account = self.accounts.get(account_id as usize).unwrap();
let mut balance_lock = account.balance.lock().unwrap();
*balance_lock += amount as i64;
self.record_succ(format!("Worker {worker_id} completed ledger {ledger_id}: deposit {} into account {account_id}", *balance_lock));
}
}
main.rs
use std::{
process,
thread,
env,
};
use crate::bank::*;
pub mod bank;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
eprintln!("Usage: {} <num_of_threads>\n", args[0]);
process::exit(1);
}
let num = args[1].parse::<i32>().unwrap_or(-1);
let bank = bank::init(10);
let mut workers = Vec::new();
for id in 0..num {
// ERROR on move: move occurs because `bank` has type `bank::Bank`, which does not implement the `Copy` trait
let worker = thread::spawn(move || worker(id, &bank));
workers.push(worker);
}
for worker in workers {
worker.join().unwrap();
}
}
fn worker(worker_id: i32, bank: &Bank) {
let mut size = i32::MAX;
bank.deposit(worker_id, 0, 1, 800);
}
The goal is to lock specific account balances and modify in parallel. I have several methods for the Bank Struct that will do these operations on self.
worker(id, l, &bank)would fail due to&banknot beingCopy—bankis behind a reference, and references areCopy, so it shouldn't matter. Can you post the full error message?Mutex<primitive>and using different mutexes for different fields may be a great mistake, if you need to update some fields together this could be a source of synchronization problems.