Reference: Why a "ownership test" is necessary in pin_mut?
In Rust for Rustaceans, Jon Gjengset gave the following codes for the pin_mut!() macro.
macro_rules! pin_mut { ($var:ident) => { let mut $var = $var; let mut $var = unsafe { Pin::new_unchecked(&mut $var) }; } }
The book claims:
The shadowing of
$varensures that the caller cannot drop thePinand continue to use the unpinned value (which would breach thePincontract for any target type that's!Unpin). By moving the value stored in$var, the macro also ensures that the caller cannot drop the$varbinding the macro declarations without also dropping the original variable.
$var ensures that the caller cannot 'drop' the Pin and continue to use the unpinned value?My thoughts:
(1) I suppose it is because once the shadowed value of type Pin<T> is dropped, the underlying value of T is dropped as well, hence, once Pin<T> is dropped, the user can no longer use the value of T.
(2) And I suppose the macro works because once the $var is shadowed, any use of the variable $var will only refer to $var that is of type Pin<T> and not the underlying value of type T.
(3) And this suggests to me that the shadowing of $var makes the underlying value of type T 'anonymous' in a way.
Assuming that I am right with my thoughts on Question One, then I need to have Question Two answered, like so:
Pin<T> leads to the dropping of the underlying value of type T?If the variable $var is shadowed and owns value of Pin<T>, it makes sense that once $var lifetime ends, Pin<T> gets dropped. But who owns the value of type T? Pin<T> doesn't own T because its constructor takes a 'pointer' type (e.g. Box<T>, &mut T etc). If nobody owns it, then who drops it? If a variable owns it, then why can't we somehow access it during compile time?
Very quick introduction: Pin is a form of contract.
A Pin is a pointer to an object that lies on a portion of memory that is guaranteed to not be moved, reallocated, invalidated. This unless the object is implementing Unpin trait (in that case Pin is just like a normal pointer, you can pretend Pin does not even exist).
In other simply words, once Pin is created, the object (Target of the pointer) will remain in the same memory address (stable) for its entire lifetime.
This might sound like a strange requirement, but it is fundamental in different scenarios (e.g., in async code where await can interrupt the execution and when resuming we want to be sure the memory has not been moved in the meanwhile).
How does the shadowing of $var ensures that the caller cannot 'drop' the Pin and continue to use the unpinned value?
Simply because you don't have access to the underlying/pointee object anymore.
Once Pin is dropped, there is no way you can get back access to the shadowed variable. To be more precise, once "pinned" you cannot even access the underlying objected. The access to the object is exclusively made via Pin-pointer which acts like a "read-only" memory access (and thus you cannot invalidate the memory) unless Unpin.
(1) I suppose it is because once the shadowed value of type Pin is dropped, the underlying value of T is dropped as well, hence, once Pin is dropped, the user can no longer use the value of T.
Not exactly. Once the Pin is dropped, the underlying value is not guaranteed to be immediately dropped. For example, as the underlying object is stack allocated, it might be dropped-deallocated on stack unwind, or in general, when block scope ends.
The idea is that when Pin is dropped, you cannot anyway access the underlying object, and thus you cannot alter the memory.
How does the dropping of Pin leads to the dropping of the underlying value of type T?
As said before, dropping the Pin does not imply dropping the underlying value.
The object is allocated on the stack. Its lifespan is associated with the scope block. Putting on your terms, we could say the "owner" is the shadowed variable that you don't have access anymore.
Variable shadowing does not imply dropping the object.
For example:
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!("Foo dropped");
}
}
fn main() {
let v = Foo;
println!("A");
let v = Foo; // <--- we are shadowing previous `v`, but this does not drop existing `v`!
println!("B");
}
Prints:
A
B
Foo dropped
Foo dropped
And not
A
Foo dropped
B
Foo dropped
(live example)
You are partially right. It is correct that the shadowing makes the original value inaccessible, but it is not true that Pin is responsible for its drop. The new variable (let $var = $var) is responsible for that.
If a variable owns it, then why can't we somehow access it during compile time?
We cannot access the original variable, because the value is moved out of it. And we cannot use the newly declared $var, because it is shadowed by let mut $var = unsafe { Pin::new_unchecked(&mut $var) };. So we cannot access the owned value, only through Pin.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With