Last active
November 6, 2017 19:09
-
-
Save mikeyhew/1ce3a5b731ae0011d1532cf8340a9ee5 to your computer and use it in GitHub Desktop.
`&move` references and an `Own` trait for rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(arbitrary_self_types, unboxed_closures, fn_traits)] | |
use std::mem::{self, ManuallyDrop}; | |
use std::ptr; | |
use std::slice; | |
macro_rules! mk_move { | |
(let $name:ident = &move $expr:expr;) => { | |
mk_move!(let $name: _ = &move $expr;); | |
}; | |
(let mut $name:ident = &move $expr:expr;) => { | |
mk_move!(let mut $name: _ = &move $expr;); | |
}; | |
(let $name:ident : $ty:ty = &move $other:ident;) => { | |
compile_error!("taking `&move name` is not supported. Try `&move (name)`, or move the mk_move! invocation up to where the name is declared."); | |
}; | |
(let $name:ident : $ty:ty = &move | $($tt:tt)*) => { | |
compile_error!("`&move ||{}` syntax is not allowed because the compiler would parse it as `&(move || {})`. Use `&move (||{})` instead.") | |
}; | |
(let $name:ident : $ty:ty = &move || $($tt:tt)*) => { | |
mk_move!(let $name: $ty = &move |); | |
}; | |
(let $name:ident : $ty:ty = &move $expr:expr;) => { | |
let mut _slot = $crate::ManuallyDrop::new($expr); | |
let $name: $ty = unsafe { | |
$crate::Move::new(&mut *_slot) | |
}; | |
}; | |
(let mut $name:ident : $ty:ty = &move $expr:expr;) => { | |
mk_move!(let $name: $ty = &move $expr;); | |
let mut name = name; | |
}; | |
} | |
trait Own<T: ?Sized> { | |
fn consume<F, Output>(self, consumer: F) -> Output | |
where | |
F: FnOnce(Move<T>) -> Output; | |
} | |
impl<T> Own<T> for T { | |
fn consume<F, Output>(self, consumer: F) -> Output | |
where | |
F: FnOnce(Move<T>) -> Output | |
{ | |
mk_move! { | |
let me = &move (self); | |
} | |
consumer(me) | |
} | |
} | |
impl<T: ?Sized> Own<T> for Box<T> { | |
fn consume<F, Output>(self, consumer: F) -> Output | |
where | |
F: FnOnce(Move<T>) -> Output | |
{ | |
let size = mem::size_of_val(&self); | |
let ptr = Box::into_raw(self); | |
unsafe { | |
let slice: &mut [u8] = slice::from_raw_parts_mut(ptr as *mut u8, size); | |
// create the box now, so it gets dropped if consumer() panics | |
let _will_drop = Box::from_raw(slice); | |
consumer(Move::new(&mut *ptr)) | |
} | |
} | |
} | |
impl<T> Own<[T]> for Vec<T> { | |
fn consume<F, Output>(mut self, consumer: F) -> Output | |
where | |
F: FnOnce(Move<[T]>) -> Output | |
{ | |
let slice: *mut [T] = &mut *self; | |
unsafe { | |
self.set_len(0); | |
consumer(Move::new(&mut *slice)) | |
} | |
} | |
} | |
impl<'a, T: ?Sized + 'a> Own<T> for Move<'a, T> { | |
fn consume<F, Output>(self, consumer: F) -> Output | |
where | |
F: FnOnce(Move<T>) -> Output | |
{ | |
consumer(self) | |
} | |
} | |
struct Move<'a, T: ?Sized + 'a>(&'a mut T); | |
impl<'a, T: ?Sized + 'a> Move<'a, T> { | |
unsafe fn new(mut_ref: &'a mut T) -> Move<'a, T> { | |
Move(mut_ref) | |
} | |
} | |
impl<'a, T: 'a> Move<'a, T> { | |
fn into_inner(self) -> T { | |
unsafe { | |
let ptr = self.0 as *const _; | |
mem::forget(self); | |
ptr::read(ptr) | |
} | |
} | |
} | |
impl<'a, T: ?Sized + 'a> Drop for Move<'a, T> { | |
fn drop(&mut self) { | |
unsafe { | |
ptr::drop_in_place(self); | |
} | |
} | |
} | |
impl<'a, T: ?Sized + 'a> std::ops::Deref for Move<'a, T> { | |
type Target = T; | |
fn deref(&self) -> &T { | |
&*self.0 | |
} | |
} | |
impl<'a, T: ?Sized + 'a> std::ops::DerefMut for Move<'a, T> { | |
fn deref_mut(&mut self) -> &mut T { | |
&mut *self.0 | |
} | |
} | |
trait FnMove<Args>: FnOnce<Args> { | |
extern "rust-call" fn call_move(self: Move<Self>, args: Args) -> Self::Output; | |
} | |
impl<F, Args> FnMove<Args> for F where F: FnOnce<Args> { | |
extern "rust-call" fn call_move(self: Move<Self>, args: Args) -> Self::Output { | |
self.into_inner().call_once(args) | |
} | |
} | |
fn main() { | |
mk_move! { | |
let f: Move<FnMove()> = &move (||{}); | |
} | |
f.call_move(()); | |
let bx = Box::new(|| println!("Weeeeeee &move-references FTW!")) as Box<FnMove()>; | |
<Box<FnMove()> as Own<FnMove()>>::consume(bx, |f| { | |
f.call_move(()) | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment