1

Part of the assert_eq macro code is:

    ($left:expr, $right:expr, $($arg:tt)+) => ({
        match (&($left), &($right)) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    // The reborrows below are intentional. Without them, the stack slot for the
                    // borrow is initialized even before the values are compared, leading to a
                    // noticeable slow down.
                    $crate::panic!(r#"assertion failed: `(left == right)`
  left: `{:?}`,
 right: `{:?}`: {}"#, &*left_val, &*right_val,
                           $crate::format_args!($($arg)+))
                }
            }
        }
    });

The comment says that the reborrow is intentional and that it would somehow influence how stack is used.

What is a reborrow and how does it influence the code the compiler generates?

Eduard Wirch
  • 9,785
  • 9
  • 61
  • 73
  • I suspect the compiler might try to re-use the `*left_val` and `*right_val` if we don't reborrow. And to be able to re-use easily it makes space for those values before the `if` which means that even if the condition is false the compiler puts space aside. I might be totally wrong though.. – Hadus Feb 24 '21 at 00:42

1 Answers1

2

I am not sure how it affects compilation but a reborrow is when you dereference something and then borrow it straight after.

For example:

fn main() {
    let mut vec: Vec<u8> = vec![1, 2, 3];
    let mut_ref: &mut Vec<u8> = &mut vec;
    let reborrow: &Vec<u8> = &*mut_ref;
}

It can be used to:

  • get an immutable reference from a mutable reference (code above)
  • get a reference with a shorter lifetime
  • get a reference to something in a smart pointer
  • get a reference from a pointer
  • and probably other things too.. (I am fairly new to Rust )

Example use for shortening lifetime (very contrived but this is oversimplified):

fn main() {
    let mut outer_vec = vec![1, 2, 3];
    let outer_ref = &mut outer_vec;

    {
        // shorten lifetime of outer_ref
        // not doing this causes an error
        let mut outer_ref = &mut *outer_ref; // mutable reborrow

        let mut inner_vec = vec![1, 2, 3];
        let inner_ref = &mut inner_vec;

        // imagine a real condition
        if true {
            outer_ref = inner_ref;
        }

        // use outer_ref which could point to the outer_vec or inner_vec
        println!("{:?}", outer_ref);
    }

    println!("{:?}", outer_ref);
}

Getting a reference to something behind a smart pointer (Box in this case):

fn main() {
    let vec = vec![1, 2, 3];
    let boxed = Box::new(vec);

    let reborrow: &Vec<u8> = &*boxed;

    println!("{:?}", reborrow);
}

Reference from pointer:

fn main() {
    let num: u8 = 10;
    let pointer: *const u8 = &num as *const u8;

    unsafe {
        let ref_from_ptr: &u8 = &*pointer;
    }
}

Not sure if all of these are strictly considered a "reborrow" by the way.

Hadus
  • 1,551
  • 11
  • 22