1

Warning, very novice Rust coder here. I'm sure this is just a basic misunderstanding of how ownership works on my end but i cant seem to wrap my head around why this code runs into error[E0502]: cannot borrow self.bids as mutable because it is also borrowed as immutable.

UPDATE: have changed the code snippet to something reproducible.

fn main() {
    #[derive(Debug, Default)]
    struct OrderBook {
        bids: Vec<Order>
    }
    #[derive(Debug)]
    struct Order {
        price: f64,
        quantity: i64,
    }

    impl OrderBook {
        pub fn add_order(&mut self, order: Order) -> Result<(), &str> {
            match self.orderbook_checks() {
                Ok(_) => {
                    self.bids.push(order);
                    Ok(())
                }
                Err(e) => Err(e),
            }
        }
        pub fn orderbook_checks(&self) -> Result<(), &str> {
            Err("error message")
        }
    }
    
    let mut order_book = OrderBook::default();
    let price: f64 = 10.0;
    let quantity: i64 = 100;
    let order = Order { price, quantity };

    order_book.add_order(order);
}

I would think that the borrow of self in the Ok() arm of the match statement on "checks" will only happen when checks evaluates to Ok() and in the event of Err(e) it should be allright to return Err(e). However this code only compiles when I return a string literal and not when using the error message value e like i do in the code snippet.

Hope anyone of you wants enlighten me on what is wrong in my thinking. I have taken a look at similar questions but I cant figure it out yet.

Compiler output below.

error[E0502]: cannot borrow `self.bids` as mutable because it is also borrowed as immutable
  --> src/main.rs:16:21
   |
13 |         pub fn add_order(&mut self, order: Order) -> Result<(), &str> {
   |                          - let's call the lifetime of this reference `'1`
14 |             match self.orderbook_checks() {
   |                   ----------------------- immutable borrow occurs here
15 |                 Ok(_) => {
16 |                     self.bids.push(order);
   |                     ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
19 |                 Err(e) => Err(e),
   |                           ------ returning this value requires that `*self` is borrowed for `'1`
4
  • Can you include the Rust compiler's output in your question so that it's clear where the error is? It might also help to show the definition of whatever type self is. Commented Jan 16, 2023 at 12:49
  • Sure, included the output of one of the "cannot borrow...". Thanks for thinking with me. Commented Jan 16, 2023 at 13:10
  • The error is showing a line that is not in your code, so it's hard to say for sure, but it looks like the orderbook_checks method borrows from self and stays borrowed until the scope where checks is defined ends. If you can amend your question to include a fully reproducible example of the error, it should help. Commented Jan 16, 2023 at 13:11
  • Have changed the code snippet. Hope this makes the issue a bit more clear. Commented Jan 16, 2023 at 13:49

1 Answer 1

1

This error stems from the fact that you have not explicitly annotated the lifetimes, and therefore Rust figures out on itself some lifetimes that do not work. This is because, in a method like fn orderbook_checks(&self) -> Result<(), &str>, without lifetime annotations, Rust gives the same lifetime to &self and to &str which is, when you think about it, completely false: the &str returned does not depend on &self being still valid. That is, it is equivalent to fn orderbook_checks<'a>(&'a self) -> Result<(), &'a str>.

In fact, in your example, since all strings are hard-coded, you could simply given them a 'static lifetime:

fn main() {
    #[derive(Debug, Default)]
    struct OrderBook {
        bids: Vec<Order>
    }
    #[derive(Debug)]
    struct Order {
        price: f64,
        quantity: i64,
    }

    impl OrderBook {
        pub fn add_order(&mut self, order: Order) -> Result<(), &'static str> {
            match self.orderbook_checks() {
                Ok(_) => {
                    self.bids.push(order);
                    Ok(())
                }
                Err(e) => Err(e),
            }
        }
        pub fn orderbook_checks(&self) -> Result<(), &'static str> {
            Err("error message")
        }
    }
    
    let mut order_book = OrderBook::default();
    let price: f64 = 10.0;
    let quantity: i64 = 100;
    let order = Order { price, quantity };

    order_book.add_order(order);
}

See the playground.

This works because, now, when you call self.orderbook_checks(), Rust doesn't have to borrow self for as much as the &str you could return would have to live; which, because of the same rule, is the same as &mut self in add_order(&mut self, order: Order) -> Result<(), &str>.

Sign up to request clarification or add additional context in comments.

2 Comments

Aah wow, this is awesome. Lifetimes is something I can not yet wrap my head around fully. But now I now why this is not working. Time to do some further study on lifetimes. Thanks a lot!
@maryn lifetimes are a very important aspect of Rust, but the problem you had is quite subtle (it requires knowing the rules Rust uses to give lifetimes when you don't explicitly give them yourself). I would recommend reading the Rust Book, as it covers lifetimes and basically everything that is important in Rust.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.