Created
July 25, 2015 09:47
-
-
Save diwic/c6cf5a625699dc13418b to your computer and use it in GitHub Desktop.
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
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