1

The following code doesn't pass the borrow checker, for Label-A uses a value that consumed by Label-B, but the code is actually safe: the Label-A is guarded with processed which is only set if Label-B is run.

How can I tell the compiler the dependency, or if I cannot, what's the idiom to solve this issue?

(Making X Copy/Clone is not acceptable, nor making consume taking a reference, neither Rc<X> is appealing (the data structure is already quite complicate))


struct X(i32);

fn consume1(_x: X) {
    ()
}

fn consume2(_x: X) {
    ()
}

fn predicate(_x: &X) -> bool {
    true
}

pub fn main() {
    let xs = vec![X(1), X(2)];
    
    for x in xs {
        let mut processed = false;

        // for _ in _ {
        if predicate(&x) {
            consume1(x); // Label-B
            processed = true;
        }
        // } end for
        // this for loop here is just to show that the real code
        // is more complicated, the consume1() is actually called
        // (somehow) inside this inner loop

        // some more code
        
        if !processed {
            consume2(x);  // Label-A
        }
    }
}

Incömplete
  • 853
  • 8
  • 20
  • Does an `if predicate { } else { }` work? – Thilo Feb 21 '21 at 02:10
  • @Thilo For the code in the question, yes, but unfortunately the real code is more complicated, I cannot put an `else` directly after the `if`. I've updated the code to reflect this, thanks. – Incömplete Feb 21 '21 at 02:20
  • 4
    If you want static code path analysis to work, you may need to restructure your code a bit to get closer to that `if/else` setup. Compilers cannot reverse-engineer complex logical dependencies. – Thilo Feb 21 '21 at 02:23
  • I'm not sure I followed your question exactly(real code might help), but you can put stuff in a Option<> and then move out of the option with `take`. – PiRocks Feb 21 '21 at 02:28
  • @Thilo I guess I have to restructure the code (which will make the business logic a little more convoluted), but it is worth asking, thanks. – Incömplete Feb 21 '21 at 02:30

1 Answers1

0

Unless I've misunderstood I think the best option for you is to use 'Option'. This way you can also get rid of that boolean flag.

struct X( i32 );

fn consume1( _x: X ) { }

fn consume2( _x: X ) { }

fn predicate( _x: &X ) -> bool {
    true
}

pub fn main( ) {
    let xs = vec![ Some( X( 1 ) ), Some( X( 2 ) ) ];

    for mut x in xs {
        if predicate( x.as_ref( ).unwrap( ) ) {
            consume1( x.take( ).unwrap( ) );
        }   
        if let Some( x ) = x {
            consume2( x );
        }
    }
 }
WBuck
  • 5,162
  • 2
  • 25
  • 36
  • Thanks for the answer, this is indeed a good solution if `xs` has an `Option` in it. (I decided to go with Thilo's comment - restructure the code) – Incömplete Feb 21 '21 at 03:46