Skip to content

Instantly share code, notes, and snippets.

@eddyb

eddyb/move.rs Secret

Last active August 29, 2015 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eddyb/82a442f0ef4d137a762e to your computer and use it in GitHub Desktop.
Save eddyb/82a442f0ef4d137a762e to your computer and use it in GitHub Desktop.
#[lang="move"]
struct Move<Sized? T, S: MoveSource<T>> {
ptr: *mut T,
src: S
}
#[lang="move_source"]
trait MoveSource<Sized? T> {
unsafe fn cleanup(self, ptr: *mut T) {}
}
#[lang="move_stack"]
struct Stack<'a>;
impl<'a, T> MoveSource<T> For Stack<'a> {
// No need to "deallocate" stack values.
unsafe fn cleanup(self, _: *mut T) {}
}
impl<Sized? T, S: MoveSource<T>> Move<T, S> {
pub unsafe fn from(ptr: *mut T, src: S) -> Move<T, S> {
Move {
ptr: ptr,
src: src
}
}
pub unsafe fn into_parts(self) -> (*mut T, S) {
let (ptr, src) = (self.ptr, self.src);
mem::forget(self);
(ptr, src)
}
}
impl<Sized? T, S: MoveSource<T>> Drop for Move<T, S> {
fn drop(&mut self) {
drop_in_place(self.ptr);
// Maybe MoveSource should require Self be Copy?
self.src.cleanup(self.ptr);
}
}
trait DerefMove<S: MoveSource<Self>> {
type Out;
type OutSrc: MoveSource<Out>;
fn deref_move(self: Move<Self, S>) -> Move<Out, OutSrc>;
}
impl<Sized? T, S> DerefMove<S> for Box<T> {
type Out = T;
type OutSrc = Heap;
fn deref_move(self: Move<Box<T>, S>) -> Move<T, Heap> {
// The way this works, {**box box foo} will free the outer box,
// move the value out, and only then it would free the inner box.
// The outer box can be freed early because it's cheaper and more
// flexible to pass around Move<Box<Foo>, Heap>, rather than the
// Move<Box<Box<Foo>>, Stack<'a>> that the compiler creates.
let the_box: Box<T> = *self;
unsafe {
Move::from(transmute::<Box<T>, *mut T>(the_box), Heap)
}
}
}
struct Heap;
impl<Sized? T> MoveSource<T> for Heap {
unsafe fn cleanup(self, ptr: *mut T) {
heap::deallocate(data_pointer(ptr), size_of_val(&*ptr));
}
}
impl<T, S> DerefMove<S> for Vec<T> {
type Out = [T];
type OutSrc = HeapSized;
fn deref_move(self: Move<Vec<T>, S>) -> Move<T, HeapSized> {
let vec: *mut [T] = &mut *self;
let size = self.capacity * mem::size_of::<T>();
unsafe {
mem::forget(self);
Move::from(vec, HeapSized(size))
}
}
}
struct HeapSized(uint);
impl<Sized? T> MoveSource<T> for HeapSized {
unsafe fn cleanup(self, ptr: *mut T) {
heap::deallocate(data_pointer(ptr), self.0);
}
}
struct MoveItems<T, S: MoveSource<[T]>> {
start: *mut T,
end: *mut T,
// This might be better off as a per-type-implementing-MoveSource
// custom "bundled dtor". Would be zero-sized for Stack and just
// (*mut T, uint) (w/o the length of *mut [T]) for HeapSized.
alloc: *mut [T],
src: S
}
impl<T, S: MoveSource<[T]>> IntoIter for Move<[T], S> {
type Iter = MoveItems<T, S>;
fn into_iter(self) -> MoveItems<T, S> {
unsafe {
let (start, len) = (self.as_mut_ptr(), self.len());
let (alloc, src) = Move::into_parts(self);
MoveItems {
start: start,
end: start.offset(len),
alloc: alloc,
src: src
}
}
}
}
impl<T, S: MoveSource<[T]>> Drop for MoveItems<T, S> {
fn drop(&mut self) {
for _ in self {}
self.src.cleanup(self.alloc);
}
}
@arielb1
Copy link

arielb1 commented Nov 26, 2014

MoveSource needs to be an RFC19 unsafe impl, in addition to cleanup being an unsafe function.

Bikeshed: I think MoveSource should be called Dealloc or Cleanup and Move be called Root.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment