3

I am new to Rust. Here is a piece of code for computer stock transaction. The Strategy will buy some stocks when the SignalTrigger triggers, and sell those stocks if after 30s/90s in different way. The code can't be compiled. Here is the code:

use std::cmp;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::collections::BinaryHeap;
use std::convert::TryFrom;
use std::error::Error;
use std::fs::File;
use std::io;
use std::process;

#[derive(Debug)]
struct Depth {
    ts: u32,
    ap_vec: Vec<f64>,
    bp_vec: Vec<f64>,
    av_vec: Vec<u32>,
    bv_vec: Vec<u32>,
}


struct Order {
    ts: u32,
    id: u32,
    is_buy: bool,
    is_mkt: bool,
    vol: u32,
    price: f64,
}

struct LongPosition {
    vol_left: u32,
    ts: u32,
}

struct Strategy {
    order_id: u32,
    prev_buy_ts: u32,
    map_orderid_position: BTreeMap<u32, LongPosition>, // map<order_id, volume_left>
}

impl Strategy {
    fn on_depth(&mut self, depth: &Depth) -> Vec<Order> {
        let mut orders_vec: Vec<Order> = Vec::new();
        for (order_id, long_position) in &mut self.map_orderid_position {
            if depth.ts - long_position.ts > 90 * 1000 {
                let order = self.make_order(depth.ts, false, true, long_position.vol_left, 0.0);
                orders_vec.push(order);
            } else if depth.ts - long_position.ts > 60 * 1000 {
                let order = self.make_order(depth.ts,false,true,long_position.vol_left,depth.bp_vec[0]);
                orders_vec.push(order);
            }
        }
        return orders_vec;
    }

    fn make_order(&mut self, ts: u32, is_buy: bool, is_mkt: bool, vol: u32, price: f64) -> Order {
        let order = Order {
            id: self.order_id,
            ts,
            is_buy,
            is_mkt,
            vol,
            price,
        };
        self.order_id = self.order_id + 1;
        return order;
    }
}

fn main() {
    let empty_price_vec: Vec<f64> = Vec::new();
    let map_orderid_position: BTreeMap<u32, LongPosition> = BTreeMap::new();
    let mut strategy = Strategy {
        prev_buy_ts: 0,
        order_id: 0,
        map_orderid_position: map_orderid_position,
    };
}

The compile says:(I have comment line 88 and line 90 in the snippet)

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src\main.rs:46:29
   |
44 |         for (order_id, long_position) in &mut self.map_orderid_position {
   |                                          ------------------------------
   |                                          |
   |                                          first mutable borrow occurs here
   |                                          first borrow later used here
45 |             if depth.ts - long_position.ts > 90 * 1000 {
46 |                 let order = self.make_order(depth.ts, false, true, long_position.vol_left, 0.0);
   |                             ^^^^ second mutable borrow occurs here

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src\main.rs:49:29
   |
44 |         for (order_id, long_position) in &mut self.map_orderid_position {
   |                                          ------------------------------
   |                                          |
   |                                          first mutable borrow occurs here
   |                                          first borrow later used here
...
49 |                 let order = self.make_order(depth.ts,false,true,long_position.vol_left,depth.bp_vec[0]);
   |                             ^^^^ second mutable borrow occurs here

error: aborting due to 2 previous errors; 12 warnings emitted

For more information about this error, try `rustc --explain E0499`.
error: could not compile `greeting`

I am a little confused about Rust, snippets like these are really common in other languages. Can you make an explanation and tell me how to fix(avoid) this situation?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
YNX
  • 511
  • 6
  • 17

3 Answers3

6

You are borrowing the whole instance when you are calling self.make_order. The compiler cant be sure that you are not going the change map_orderid_position. Instead, you can create a standalone function and pass the mutable reference to order_id field to it.

impl Strategy {
    fn on_depth(&mut self, depth: &Depth) -> Vec<Order> {
        let mut orders_vec: Vec<Order> = Vec::new();
        for (order_id, long_position) in &mut self.map_orderid_position {
            if depth.ts - long_position.ts > 90 * 1000 {
                let order = make_order(depth.ts, false, true, long_position.vol_left, 0.0, &mut self.order_id);
                orders_vec.push(order);
            } else if depth.ts - long_position.ts > 60 * 1000 {
                let order = make_order(depth.ts,false,true,long_position.vol_left,depth.bp_vec[0], &mut self.order_id);
                orders_vec.push(order);
            }
        }
        return orders_vec;
    }

    
}

fn make_order(ts: u32, is_buy: bool, is_mkt: bool, vol: u32, price: f64, order_id: &mut u32) -> Order {
    let order = Order {
        id: *order_id,
        ts,
        is_buy,
        is_mkt,
        vol,
        price,
    };
    *order_id += 1;
    return order;
}
Gurwinder Singh
  • 38,557
  • 6
  • 51
  • 76
2

Instead of updating self.order_id in the function make_order() you can update it in the function on_depth(). Then you don't have to use &mut self for make_order() and the problem is solved.

Example:

impl Strategy {
    fn on_depth(&mut self, depth: &Depth) -> Vec<Order> {
        let mut orders_vec: Vec<Order> = Vec::new();
        for (order_id, long_position) in &self.map_orderid_position {
            let order = if depth.ts - long_position.ts > 90 * 1000 {
                self.make_order(depth.ts, false, true, long_position.vol_left, 0.0)
            } else if depth.ts - long_position.ts > 60 * 1000 {
                self.make_order(depth.ts, false, true, long_position.vol_left, depth.bp_vec[0])
            } else {
                continue;
            };
            orders_vec.push(order);
            self.order_id += 1; // Update `self.order_id` here
        }
        return orders_vec;
    }

    // Changed `&mut self` to `&self`
    fn make_order(&self, ts: u32, is_buy: bool, is_mkt: bool, vol: u32, price: f64) -> Order {
        let order = Order {
            id: self.order_id,
            ts,
            is_buy,
            is_mkt,
            vol,
            price,
        };
        // Removed `self.order_id = self.order_id + 1`
        return order;
    }
}
yolenoyer
  • 8,797
  • 2
  • 27
  • 61
0

From the book:

The benefit of having this restriction is that Rust can prevent data races at compile time. A data race is similar to a race condition and happens when these three behaviors occur:

Two or more pointers access the same data at the same time.
At least one of the pointers is being used to write to the data.
There’s no mechanism being used to synchronize access to the data.

Data races cause undefined behavior and can be difficult to diagnose and fix when you’re trying to track them down at runtime; Rust prevents this problem from happening because it won’t even compile code with data races!

I don't know what the best practice is but I would generally make a clone of the variable for reading, for, example:

fn main() {
    let mut s = String::from("hello");

    let r1 = s.clone();   // if this was &mut s, I'll get the same error
    let r2 = &mut s;

    println!("{}, {}", r1, r2);
}

So, you could probably try making clone of self.map_orderid_position since you are just reading values from it.

unixia
  • 4,102
  • 1
  • 19
  • 23