Skip to content

Instantly share code, notes, and snippets.

@diwic
Created July 25, 2015 09:47
Show Gist options
  • Save diwic/c6cf5a625699dc13418b to your computer and use it in GitHub Desktop.
Save diwic/c6cf5a625699dc13418b to your computer and use it in GitHub Desktop.
use std::{ptr, mem, ops};
/// Bx is a Box-like container, but differs in several important ways:
///
/// When constructing a Bx, you first get a BxInit.
/// You can then get an immutable reference to the final Bx's interior,
/// if you promise not to use it until you have called BxInit.init().
///
/// There is no way to get a mutable reference to Bx's interior.
/// Bx does not implement the magic DerefMove trait, so an owner of Bx<T>
/// cannot move T (and thus cause dangling internal references to T).
///
/// There is no memory overhead compared to Box.
///
pub struct Bx<T>(Box<T>);
pub struct BxInit<T>(*mut T);
impl<T> Bx<T> {
pub fn new() -> BxInit<T> {
unsafe {
let b: Box<T> = Box::new(mem::uninitialized());
BxInit(mem::transmute(b))
}
}
}
impl<T> ops::Deref for Bx<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&*self.0
}
}
impl<T> BxInit<T> {
/// This function is highly unsafe, but also highly useful.
/// The rules are, you promise to only derefence this reference
/// after you've called init(), and before the Bx has been
/// destroyed.
pub unsafe fn unsafe_ref(&self) -> &'static T {
mem::transmute(self.0)
}
/// Finalize initialization.
pub fn init(self, t: T) -> Bx<T> {
unsafe {
ptr::copy_nonoverlapping(&t, self.0, 1);
mem::forget(t);
Bx(mem::transmute(self.0))
}
}
// Note: if a BxInit is dropped without being turned into a Bx,
// then memory might leak. This is safe, but undesirable.
}
#[test]
fn test_drop() {
use std::cell::Cell;
let dropped = Cell::new(0u32);
struct Dummy<'a>(&'a Cell<u32>);
impl<'a> Drop for Dummy<'a> {
fn drop(&mut self) { self.0.set(self.0.get()+1) }
}
{
let m = Bx::<Dummy>::new();
assert_eq!(dropped.get(), 0);
m.init(Dummy(&dropped));
assert_eq!(dropped.get(), 0);
}
assert_eq!(dropped.get(), 1);
}
#[test]
fn test_deref() {
let m: BxInit<u32> = Bx::new();
let ur = unsafe { m.unsafe_ref() };
let m = m.init(83);
let sr = &*m;
assert_eq!(sr as *const u32, ur as *const _);
assert_eq!(*sr, 83);
assert_eq!(*ur, 83);
}
#[cfg(test)]
mod example {
use super::Bx;
use std::cell::Cell;
struct Car { wheels: Vec<Wheel>, speed: Cell<u32> }
struct Wheel(&'static Car);
impl Car {
// There is no way Wheel could reference an dropped car:
// Bx (unlike Box) does not allow DerefMove behaviour (Car cannot move out of the Bx)
// Destroying Bx or calling bx_clear will destroy both the Car and its Wheels
pub fn new() -> Bx<Car> {
let bx = Bx::new();
let cref = unsafe { bx.unsafe_ref() };
let car = Car {
speed: Cell::new(0),
wheels: vec![Wheel(cref), Wheel(cref), Wheel(cref), Wheel(cref)]
};
bx.init(car)
}
// Some dummy functions to make use of references in both directions
pub fn accelerate(&self) { for w in &self.wheels { w.accelerate() }}
pub fn get_speed(&self) -> u32 { self.speed.get() }
}
impl Wheel { fn accelerate(&self) { self.0.speed.set(self.0.speed.get() + 1) }}
#[test]
fn test_car() {
let c = Car::new();
c.accelerate();
assert_eq!(c.get_speed(), 4);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment