Is there a better way wrote such code to avoid double mutable borrow rather than wrote things like `if if`?

I found a code fragment that must use double if, which is very strange for me.

                    if if let Some(mut x)=heap.peek_mut(){
                        suppose_it_returns_a_boolean(x)
                    } else {
                        // some other logic
                        break
                    } {
                        heap.pop();
                    }

In short, I want to either modify or pop an item from a heap at some condition. when I tried pop it, an error occurs:

                    if let Some(mut x)=heap.peek_mut(){
                        if suppose_it_returns_a_boolean(x) {
                            heap.pop(); // boom, heap is mutable borrowed in `heap.peek_mut()`, here is a second borrow.
                        }
                    } else {
                        // some other logic
                        break
                    }

Is there some better way to wrote such implementation?

What's more, is it possible to combine if-let chain with normal conditions? In the same question, I have to wrote the same fragment twice:

                    if let Some(mut x)=finish.peek_mut(){
                        if x[0]+price>=0 {
                            amount-=x[1];
                            if amount<0 {
                                x[1]=-amount;
                                false
                            } else {
                                true
                            }
                        } else {
                            append.push([price,amount]);
                            break
                        }
                    } else {
                        append.push([price,amount]);
                        break
                    }

is there some syntax sugar that allow us to wrote things like:

                    if (let Some(mut x)=finish.peek_mut()) && x[0]+price>=0 {
                        amount-=x[1];
                        if amount<0 {
                            x[1]=-amount;
                            false
                        } else {
                            true
                        }
                    } else {
                        append.push([price,amount]);
                        break
                    }

the question is here full code is shown below:

impl Solution {
    pub fn get_number_of_backlog_orders(orders: Vec<Vec<i32>>) -> i32 {
        let [mut buy,mut sell]=[std::collections::BinaryHeap::<[i32;2]>::new(),std::collections::BinaryHeap::<[i32;2]>::new()];
        orders.iter().for_each(|x|{
            if let [mut price,mut amount, order_type]=x[0..3]{
                let [finish,append]=if order_type==1 {
                    price=-price;
                    [&mut buy,&mut sell]
                }else{
                    [&mut sell,&mut buy]
                };
                while amount>0 {
                    if if let Some(mut x)=finish.peek_mut(){
                        if x[0]+price>=0 {
                            amount-=x[1];
                            if amount<0 {
                                x[1]=-amount;
                                false
                            } else {
                                true
                            }
                        } else {
                            append.push([price,amount]);
                            break
                        }
                    } else {
                        append.push([price,amount]);
                        break
                    } {
                        finish.pop();
                    }
                }
            }
        });
        ((buy.into_iter().map(|[a,b]|b as i64).sum::<i64>()+sell.into_iter().map(|[a,b]|b as i64).sum::<i64>()) % 10_0000_0007i64) as i32
    }
}

Every time I see it, I’m still confused by the stupid (highly unidiomatic) impl Solution { } dance that leetcode still does here for Rust, as if this was Java and we didn’t have free-standing functions.

8 Likes

You can use match instead, and pull its result out into a separate variable:

let flag = match the_optional {
    Some(inner) => returns_a_bool(inner),
    None => false
};
if flag {
    ...
}

actually I could just put all things in the second if into such flag, no need to wrote match here.

                    let flag=if let Some(mut x)=heap.peek_mut(){
                        suppose_it_returns_a_boolean(x)
                    } else {
                        // some other logic
                        break
                    };
                    if flag {
                        heap.pop();
                    }

But it still seems ugly. We just define a garbage variable that is absolutely nonsense.

What's more, what is the benefit that use match clause?

I tried match, but using match does not allow us combine match with common conditions. I have to wrote the silly fragment

{
    append.push([price,amount]);
    break
}

twice.

Well, if there is additional control flow (namely, the else branch always diverges due to the break), then you don't even need a variable. The whole snippet above is identical to

if let Some(mut x) = heap.peek_mut() {
    if suppose_it_returns_a_boolean(x) {
        heap.pop();
    }
} else {
    // some other logic
    break;
}

I don't know what you mean by this. Certainly, match is not less powerful than if let; in fact, match is more general. Match can have multiple (more than 2) branches, and it allows the use of guards, too. Thus, anything you could do with if let, you can also do with match.

2 Likes

This works:

while amount > 0 {
    let mut peeked = finish.peek_mut();
    if let Some(x) = &mut peeked {
        if x[0] + price >= 0 {
            amount -= x[1];
            if amount < 0 {
                x[1] = -amount;
            } else {
                drop(peeked);
                finish.pop();
            }
        } else {
            append.push([price, amount]);
            break;
        }
    } else {
        append.push([price, amount]);
        break;
    }
}

Which can then become

while amount > 0 {
    let mut peeked = finish.peek_mut();
    if let Some(x) = &mut peeked {
        if x[0] + price >= 0 {
            amount -= x[1];
            if amount < 0 {
                x[1] = -amount;
            } else {
                drop(peeked);
                finish.pop();
            }
            continue;
        }
    }
    append.push([price, amount]);
    break;
}

I guess?

1 Like

Yes, use match.

match finish.peek_mut() {
    Some(mut x) if x[0] + price >= 0 => {
        amount-=x[1];
        if amount<0 {
            x[1]=-amount;
            false
        } else {
            true
        }
    }
    _ => {
        append.push([price, amount]);
        break;
    }
}
3 Likes

Ah… match is great. Combining the approaches:

while amount > 0 {
    let mut peeked = finish.peek_mut();
    match &mut peeked {
        Some(x) if x[0] + price >= 0 => {
            amount -= x[1];
            if amount < 0 {
                x[1] = -amount;
            } else {
                drop(peeked);
                finish.pop();
            }
        }
        _ => {
            append.push([price, amount]);
            break;
        }
    }
}
1 Like