Cannot borrow data in an `Arc` as mutable

I'm using actix-web to serve a http post endpoint.

async fn index<'s, 'a: 's>(order: web::Json<match_orders::Order>, data: web::Data<match_orders::Container>) -> String {
    let add_buy_order = data.add_buy_order(order.0);
    match add_buy_order {
        Some(true) => "Success".to_string(),
        Some(false) => "-1".to_string(),
        _ => "-2".to_string()
    }
}

I get an error at data.add_buy_order(order) and that function corresponds to:

#[derive(Debug, Eq, PartialEq, Deserialize, Clone, Copy)]
pub struct Order {
    pub amount_of_stock: i64,
    pub value: NotNan<f64>,
    pub stock_id: i64,
}

#[derive(Debug)]
pub struct Container {
    pub buy_container: RwLock<BinaryHeap<Order>>,
    pub sell_container: RwLock<BinaryHeap<Order>>,
    pub(crate) stocks: RwLock<HashMap<i64, Stock>>,
}

impl<'a> Exchange<'a> for Container {
    fn add_buy_order(&'a mut self, order: Order) -> Option<bool> {
        match self.buy_container.get_mut() {
            Ok(result) => result.push(order),
            Err(e) => println!("error adding buy order in buy container: {:?}", e),
        }
        Some(true)
    }
}

The error I get for the data.add_buy_order(order.0) line is:

error: cannot borrow data in an `Arc` as mutable
error: cannot borrow as mutable
help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<Container>`

I suppose I don't really care if I borrow this - I need to create a copy anyway to store it in the heap.

I've tried implementing DerefMut and Deref for Container but it didn't seem to help (presumably because I need to implement it for Arc). I read online that by wrapping the Order in a mutex, I could resolve this issue but that gave me the same error.

However, I also read that Arc is not quite idiomatic rust (or rather it was a way of getting around the borrow checker). As a new rust user, I'd also like to know what the idiomatic way of solving this issue would look like.

right, that function takes &mut self, so calling it on an Arc<Container> will indeed not work.

Interestingly, you're having RwLock fields anyways, so in theory the function doesn't actually need &'a mut self, but could work with &'a self (using RwLock::write instead of RwLock::get_mut). But it's a trait implementation, so you cannot just change the signature (unless you change the trait, or stop using the trait for this). I don't know whether that's your own trait, and whether it would make sense to change it. Another approach would be to use Arc<Mutex<Container>> or Arc<RwLock<Container>> instead of Arc<Container>. I'm personally not familiar with actix-web at all, but the documentation of Data does show one use-case of Data<Mutex<...>>. If you do this change, there's two layers of Mutex/RwLock (one around the Container, and one for each field), so in that case you should re-evaluate if the inner RwLocks are still necessary.

Arc definitely has lots of idiomatic use-cases. Also you're using API from a different crate here (actix-web) that exposes the Arc to you, so it looks like you don't even really have too much of a choice anyway on whether or not you'll need to handle an Arc.

1 Like