use std::future::Future; use std::ptr; // This test: // - Compares addresses of non-Copy data before and after moving it // - Writes to the pointer after it has moved across the await point // // This is only meant to assert current behavior, not guarantee that this is // how it should work in the future. In fact, upcoming changes to rustc // *should* break these tests. // See: https://github.com/rust-lang/rust/issues/62958 async fn data_moved_async() { async fn helper(mut data: Vec, raw_pointer: *mut Vec) { let raw_pointer2 = ptr::addr_of_mut!(data); // `raw_pointer` points to the original location where the Vec was stored in the caller. // `data` is where that Vec (to be precise, its ptr+capacity+len on-stack data) // got moved to. Those will usually not be the same since the Vec got moved twice // (into the function call, and then into the coroutine upvar). assert_ne!(raw_pointer, raw_pointer2); unsafe { // This writes into the `x` in `data_moved_async`, re-initializing it. std::ptr::write(raw_pointer, vec![3]); } } // Vec is not Copy let mut x: Vec = vec![2]; let raw_pointer = ptr::addr_of_mut!(x); helper(x, raw_pointer).await; unsafe { assert_eq!(*raw_pointer, vec![3]); // Drop to prevent leak. std::ptr::drop_in_place(raw_pointer); } } // Same thing as above, but non-async. fn data_moved() { fn helper(mut data: Vec, raw_pointer: *mut Vec) { let raw_pointer2 = ptr::addr_of_mut!(data); assert_ne!(raw_pointer, raw_pointer2); unsafe { std::ptr::write(raw_pointer, vec![3]); } } let mut x: Vec = vec![2]; let raw_pointer = ptr::addr_of_mut!(x); helper(x, raw_pointer); unsafe { assert_eq!(*raw_pointer, vec![3]); std::ptr::drop_in_place(raw_pointer); } } fn run_fut(fut: impl Future) -> T { use std::task::{Context, Poll, Waker}; let mut context = Context::from_waker(Waker::noop()); let mut pinned = Box::pin(fut); loop { match pinned.as_mut().poll(&mut context) { Poll::Pending => continue, Poll::Ready(v) => return v, } } } fn main() { run_fut(data_moved_async()); data_moved(); }