0

In this following code playgroud.

The first solution doesn't compile:

let c = manager.add_currency(Currency {
  name: "EUR".to_string(),
});

let _i = manager.add_instrument(Instrument {
  name: "RRR".to_string(),
  currency: c.clone(),
});

with this error msg

Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `manager` as mutable more than once at a time
  --> src/main.rs:59:18
   |
55 |           let c = manager.add_currency(Currency {
   |  _________________-
56 | |             name: "EUR".to_string(),
57 | |         });
   | |__________- first mutable borrow occurs here
58 | 
59 |           let _i = manager.add_instrument(Instrument {
   |  __________________^
60 | |             name: "RRR".to_string(),
61 | |             currency: c.clone(),
   | |                       --------- first borrow later used here
62 | |         });
   | |__________^ second mutable borrow occurs here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` due to previous error

according to this, i understand "c" is an reference on Rc and borrow/mutable manager because add_currency is mutable. right ?

so add_instrument want to borrow/mutable manager, there is an error

But if i update my code :

manager.add_currency(Currency {
  name: "EUR".to_string(),
});

let c = manager.get_last_currency();
let _i = manager.add_instrument(Instrument {
  name: "RRR".to_string(),
  currency: c.clone(),
});

compile without error it is same situation c is an borrow/not mutable from manager and it is impossible with a variable to borrow as mutable and un mutable in same time.

Where am I wrong ?

3 Answers3

1

I think that if you use Rc all over the place, you should not try to keep references on them. In my opinion this usage of Rc is very Java-ish (no explicit ownership, everyone shares the ownership of everything) and is not exactly if spirit of Rust (precise ownership model, borrowing...).

If you want to keep these Rcs, I suggest you clone() them when they are returned, so that we don't have to keep borrowing the structure containing them. They are designed with this usage in mind: you clone them (it's cheap, relative to cloning the data they refer to) in order to share ownership.

If you actually could keep references to these Rcs at the call site (thus keep borrowing Manager), then this would mean that you probably could get rid of these Rcs.

Of course, you don't have to clone again the obtained Rcs at the call site.

    fn add_currency(
        &mut self,
        c: Currency,
    ) -> Rc<Currency> {
        let rc = Rc::new(c);
        self.currencies.push(rc.clone());
        rc // no reference here, but a clone of the stored Rc
    }

    fn get_last_currency(&self) -> Rc<Currency> {
        self.currencies.last().unwrap().clone() // no reference here, but a clone of the stored Rc
    }

    fn add_instrument(
        &mut self,
        i: Instrument,
    ) -> Rc<Instrument> {
        let rc = Rc::new(i);
        self.instruments.push(rc.clone());
        rc // no reference here, but a clone of the stored Rc
    }
prog-fh
  • 13,492
  • 1
  • 15
  • 30
1

I believe the second (working) version is an instance of two-phased borrows. rust-lang/rust#56254 for reference. Which was recently changed from being a future incompatibility lint to being an allowed code pattern. rust-lang/rust#96268 for reference.

As such this is a special exception to the usual rules.

Skgland
  • 862
  • 10
  • 24
0

Removing the self argument from the associated add_currency() function and returning Rc<Currency>:

fn add_currency(currencies: &mut Vec<Rc<Currency>>, c: Currency) -> Rc<Currency> {
    currencies.push(Rc::new(c));
    currencies.last().cloned().unwrap()
}

Playground

Kaplan
  • 2,572
  • 13
  • 14