Skip to content

Instantly share code, notes, and snippets.

@mikeyhew
Last active November 6, 2017 19:09
Show Gist options
  • Save mikeyhew/1ce3a5b731ae0011d1532cf8340a9ee5 to your computer and use it in GitHub Desktop.
Save mikeyhew/1ce3a5b731ae0011d1532cf8340a9ee5 to your computer and use it in GitHub Desktop.
`&move` references and an `Own` trait for rust
#![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