Last active
May 31, 2017 20:11
-
-
Save mikeyhew/635afa3155440b1e2e677070bfe6fed2 to your computer and use it in GitHub Desktop.
A Vec-like data structure for trait objects
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(alloc, unsize)] | |
extern crate alloc; | |
use alloc::raw_vec::RawVec; | |
use std::marker::Unsize; | |
use std::{ptr, mem}; | |
use std::vec; | |
use std::nikomatzakis::Referent; | |
pub struct DSTVec<T: Referent + ?Sized> { | |
// these aren't actual pointers, they're offets + meta, used to construct a | |
// pointer on the fly | |
pointers: Vec<(usize, T::Meta)>, | |
data: RawVec<u8>, | |
used_bytes: usize, | |
} | |
impl<T: ?Sized> DSTVec<T> { | |
pub fn new() -> DSTVec<T> { | |
let data = RawVec::new(); | |
DSTVec { | |
pointers: Vec::new(), | |
data: data, | |
used_bytes: 0, | |
} | |
} | |
pub fn push<U: Unsize<T>>(&mut self, value: U) { | |
let align = mem::align_of::<U>(); | |
let gap = self.used_bytes + align - (self.used_bytes % align); | |
let size = mem::size_of::<U>(); | |
self.data.reserve(self.used_bytes, gap + size); | |
self.pointers.reserve(1); | |
let offset = self.used_bytes + gap; | |
let (_, meta) = Referent::disassemble(&value); | |
unsafe { | |
let back = self.data.ptr().offset(offset as isize); | |
ptr::copy_nonoverlapping(&value, back as *mut U, 1); | |
} | |
self.pointers.push((offset, meta)); | |
self.used_bytes += gap + size; | |
mem::forget(value); | |
} | |
} | |
impl<T> Drop for DSTVec<T> { | |
fn drop(&mut self) { | |
for &(offset, meta) in self.pointers.iter() { | |
unsafe { | |
let pointer = Referent::assemble(self.data.ptr().offset(offset), meta); | |
ptr::drop_in_place(pointer); | |
} | |
} | |
} | |
} | |
/// this implementation would be improved with &move references, too. | |
impl<T: ?Sized> IntoIterator for DSTVec<T> { | |
type Item = *mut T; | |
type IntoIter = IntoIter; | |
fn into_iter(mut self) -> IntoIter { | |
let pointers = mem::replace(&mut self.pointers, Vec::new()); | |
let data = mem::replace(&mut self.data, RawVec::new()); | |
IntoIter { | |
pointers: pointers.into_iter(), | |
data: data, | |
} | |
} | |
} | |
pub struct IntoIter<T: ?Sized> { | |
pointers: vec::IntoIter<(usize, T::Meta)>, | |
data: RawVec<u8>, | |
} | |
impl<T: ?Sized> Iterator for IntoIter { | |
type Item = *mut T; | |
fn next(&mut self) -> Option<*mut T> { | |
self.pointers.next().map(|(offset, meta)| { | |
unsafe { | |
Referent::assemble(self.data.ptr().offset(offset), meta) | |
} | |
}) | |
} | |
fn size_hint(&self) -> (usize, Option<usize>) { | |
self.pointers.size_hint() | |
} | |
} | |
impl ExactSizeIterator for IntoIter {} | |
impl Drop for IntoIter { | |
fn drop(&mut self) { | |
let offsets_with_metas = mem::replace(&mut self.pointers, Vec::new().into_iter()); | |
for (offset, meta) in offsets_with_metas { | |
unsafe { | |
let pointer = Referent::assemble(self.data.ptr().offset(offset), meta); | |
ptr::drop_in_place(pointer); | |
} | |
} | |
} | |
} | |
pub trait FnOnceUnsafe { | |
unsafe fn call_once_unsafe(&mut self); | |
} | |
impl<F: FnOnce()> FnOnceUnsafe for F { | |
unsafe fn call_once_unsafe(&mut self) { | |
ptr::read(self)() | |
} | |
} | |
type Callbacks = DSTVec<FnOnceUnsafe>; | |
#[test] | |
fn test_closures() { | |
let mut foo = 5; | |
let mut bar = 10; | |
let mut callbacks = Callbacks::new(); | |
callbacks.push(|| { | |
foo = 20; | |
}); | |
callbacks.push(|| { | |
bar = 30; | |
}); | |
for callback in callbacks { | |
unsafe { | |
(*callback).call_once_unsafe(); | |
} | |
} | |
assert_eq!(foo, 20); | |
assert_eq!(bar, 30); | |
} | |
fn main() { | |
let mut callbacks = Callbacks::new(); | |
callbacks.push(|| { | |
println!("Hello"); | |
}); | |
callbacks.push(|| { | |
println!("World!"); | |
}); | |
for callback in callbacks { | |
unsafe { | |
(*callback).call_once_unsafe(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This gist has been moved into a repository