Last active
June 5, 2018 06:20
-
-
Save mikeyhew/8ac8bbb77803ee239daeeffc7028a8b7 to your computer and use it in GitHub Desktop.
DerefMove and DerefInto
This file contains hidden or 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, specialization)] | |
use std::{ | |
mem::{self, ManuallyDrop}, | |
ptr::{self, NonNull}, | |
marker::PhantomData, | |
ops::{Deref, DerefMut}, | |
}; | |
struct Move<'a, T: 'a + ?Sized> { | |
ptr: NonNull<T>, | |
_marker: PhantomData<&'a mut [u8]>, | |
} | |
impl<'a, T: 'a + ?Sized> Move<'a, T> { | |
/// Constructs a Move<T> from an &mut T | |
/// will take ownership of the T, and you must | |
/// avoid the original T from being destructed | |
/// mem::ManuallyDrop is a good way to do that | |
unsafe fn from_mut(r: &mut T) -> Self { | |
Move { | |
ptr: NonNull::new_unchecked(r), | |
_marker: PhantomData, | |
} | |
} | |
} | |
impl<'a, T: ?Sized> Drop for Move<'a, T> { | |
fn drop(&mut self) { | |
unsafe { ptr::drop_in_place(self.ptr.as_ptr()) }; | |
} | |
} | |
impl<'a, T: ?Sized> Deref for Move<'a, T> { | |
type Target = T; | |
fn deref(&self) -> &T { | |
unsafe { self.ptr.as_ref() } | |
} | |
} | |
impl<'a, T: ?Sized> DerefMut for Move<'a, T> { | |
fn deref_mut(&mut self) -> &mut T { | |
unsafe { self.ptr.as_mut() } | |
} | |
} | |
impl<'a, T: ?Sized> DerefMove for Move<'a, T> { | |
fn deref_move<'b, F, Output>(self: Move<'b, Self>, f: F) -> Output | |
where | |
F: for<'c> FnOnce(Move<'c, Self::Target>) -> Output | |
{ | |
f(self.deref_into()) | |
} | |
} | |
impl<'a, T: Sized> DerefInto for Move<'a, T> { | |
fn deref_into(self) -> T { | |
let ptr = self.ptr; | |
mem::forget(self); | |
unsafe { ptr::read(ptr.as_ptr()) } | |
} | |
} | |
trait DerefInto: DerefMut { | |
fn deref_into(self) -> Self::Target; | |
} | |
trait DerefMove: DerefMut { | |
fn deref_move<'a, F, Output>(self: Move<'a, Self>, f: F) -> Output | |
where | |
F: for<'b> FnOnce(Move<'b, Self::Target>) -> Output; | |
} | |
impl<T: Sized> DerefInto for T | |
where | |
T: DerefMove, | |
T::Target: Sized, | |
{ | |
default fn deref_into(self) -> Self::Target { | |
let mut holder = ManuallyDrop::new(self); | |
let move_ref = unsafe { Move::from_mut(&mut *holder) }; | |
move_ref.deref_move(|move_ref| { | |
move_ref.deref_into() | |
}) | |
} | |
} | |
// requiring `T: Sized` until `ManuallyDrop` supports `T: ?Sized` | |
impl<T: Sized> DerefMove for Box<T> { | |
fn deref_move<'a, F, Output>(self: Move<'a, Self>, f: F) -> Output | |
where | |
F: for<'b> FnOnce(Move<'b, Self::Target>) -> Output | |
{ | |
let bx: Box<T> = self.deref_into(); | |
let mut holder: Box<ManuallyDrop<T>> = unsafe { mem::transmute(bx) }; | |
let move_ref = unsafe { Move::from_mut(&mut **holder) }; | |
f(move_ref) | |
} | |
} | |
fn main() { | |
// current Box special-casing | |
let bx = Box::new(vec![1,2,3]); | |
let vec: Vec<i32> = *bx; | |
println!("Box special-casing: {:?}", vec); | |
// same effect using DerefInto | |
let bx = Box::new(vec![4,5,6]); | |
let vec: Vec<i32> = bx.deref_into(); | |
println!("DerefInto: {:?}", vec); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment