How does compiler infer that Box is borrowing the one which its content borrows?

Viewed 61
let s: String = String::from("some message");

let b: Box<&String> = Box::<&String>::new(&s);

drop(s);

let another_box = b; // error! b (which borrows s) is used later!

So, above code looks very simple.

b is clearly borrowing s, in the human sense.

But, I think this is quite magical: How can the compiler knows that without any lifetime statements?

According to the Rust's standard library, Box's declaration is as follows:

pub struct Box<T, A = Global>(_, _)
    where A: AllocRef, T: ?Sized;

And what I think what it really is is as follows:

pub struct Box<'a, T, A = Global>(_, _)
    where A: AllocRef, T: ?Sized + 'a;

I don't think that the lifetime(or the borrowing) moves automatically for all the situations where a struct has a generic paramter type. Or is it??

1 Answers

The lifetime is part of the type T that's wrapped in the box in this case.

You wrap &s of type &'a String in the box, where 'a is an appropriate lifetime of the reference, inferred by the compiler. The type of b is therefore Box<&'a String>, so it includes the same lifetime parameter. The box itself doesn't need an explicit lifetime parameter – it's generic over an arbitrary type, and there is no reason that type needs to have static lifetime.

This is not specific to a Box. For example it works the same way for PhantomData, although PhantomData doesn't even use its type parameter in any way:

use std::marker::PhantomData;

fn make_phantom<T>(_: T) -> PhantomData<T> {
    PhantomData
}

fn main() {
    let s: String = String::from("some message");
    let phantom = make_phantom(&s);
    drop(s);
    let _another_phantom = phantom;
}

results in the same error, i.e. from the viewpoint of the borrow checker, phantom borrows s, though in reality it doesn't hold a reference to s.

(Playground)

Related