4

Here is a minimal working example, I have a NumberFactory responsible for creating special Numbers, but using it is kinda counter-intuitive to me.

struct NumberFactory {
    max_id: usize,
}

#[derive(Copy, Clone, Debug)]
struct Number {
    id: usize,
    scalar: f32,
}

impl Number {
    fn new(id: usize, scalar: f32) -> Self {
        Self { id, scalar }
    }
}

impl NumberFactory {
    fn new() -> Self {
        Self { max_id: 0 }
    }
    
    fn from_scalar(&mut self, scalar: f32) -> Number {
        let id = self.max_id;
        self.max_id += 1;
        Number::new(id, scalar)
    }
    
    fn add(&mut self, a: Number, b: Number) -> Number {
        let id = self.max_id;
        self.max_id += 1;
        Number::new(id, a.scalar + b.scalar)
    }
}

fn main() {
    let mut nf = NumberFactory::new();
    
    let x = nf.from_scalar(1.0);
    let y = nf.from_scalar(2.0);
    let z = nf.add(x, y);
    
    println!("z: {:?}", z);
}

If instead I try to write:

fn main() {
    let mut nf = NumberFactory::new();
    
    let x = nf.from_scalar(1.0);
    let z = nf.add(x, nf.from_scalar(2.0));
    
    println!("z: {:?}", z);
}

I get the error:

39 |     let z = nf.add(x, nf.from_scalar(2.0));
   |             ----------^^^^^^^^^^^^^^^^^^^-
   |             |  |      |
   |             |  |      second mutable borrow occurs here
   |             |  first borrow later used by call
   |             first mutable borrow occurs here
   |
help: try adding a local storing this argument...

I think I understand the error, in Rust's logic, but I find it weird. Wouldn't nf.from_scalar(2.0) have returned before nf.add is called, hence not borrowing nf twice at the same time?

Is it due to Rust's method calling conventions? In C, both arguments would have been put on the stack before the call to add if I remember correctly, so nf wouldn't have been borrowed twice (if the concept existed in C).

Rust's help text here is perfect here, any reason why this sort of optimization cannot be performed automatically?

cdhowie
  • 158,093
  • 24
  • 286
  • 300
djfm
  • 2,317
  • 1
  • 18
  • 34
  • Interestingly, this fails to compile even under Polonius. – user4815162342 May 16 '22 at 17:39
  • 1
    This kind of question pops up kinda often, and as I understand it, it's just that the compiler is, for the time being, overly conservative. The compiler cannot always understand if your code is sound or not, and in some corner cases, such as this one, it won't compile. Note that (in theory), the converse is not possible: it shouldn't allow you to compile code that is not sound (but it can not compile sound code). – jthulhu May 16 '22 at 18:38
  • Indeed that's what a commenter in the (perfectly appropriately) linked question said. I find it surprising that this problem be so hard, but I trust them :) – djfm May 17 '22 at 19:11

0 Answers0