use std::mem::{self, ManuallyDrop};
use std::thread;
#[derive(Debug)]
struct DropBomb();
impl Drop for DropBomb {
fn drop(&mut self) {
let message = "Trying to drop an AliasedCell, which may still have aliases outstanding.";
if thread::panicking() {
eprintln!("{}", message);
} else {
panic!("{}", message);
}
}
}
#[derive(Debug)]
pub struct AliasedCell<T> {
value: ManuallyDrop<T>,
drop_bomb: DropBomb,
}
impl<T> AliasedCell<T> {
pub fn new(value: T) -> AliasedCell<T> {
AliasedCell {
value: ManuallyDrop::new(value),
drop_bomb: DropBomb(),
}
}
pub unsafe fn alias_mut(&mut self) -> &mut T {
&mut self.value
}
pub unsafe fn alias(&self) -> &T {
&self.value }
pub unsafe fn into_inner(self) -> T {
mem::forget(self.drop_bomb);
ManuallyDrop::into_inner(self.value)
}
}
#[cfg(test)]
mod tests {
use super::AliasedCell;
unsafe fn mutate_value(addr: *mut [i32; 4]) {
let value = addr.as_mut().unwrap();
value[1] += value[3];
}
struct Mutator {
addr: *mut [i32; 4],
ascended: bool,
}
impl Mutator {
unsafe fn new(addr: *mut [i32; 4]) -> Mutator {
Mutator {
addr,
ascended: false,
}
}
fn ascend(&mut self) {
self.ascended = true;
}
unsafe fn mutate(&mut self) {
let value = self.addr.as_mut().unwrap();
if self.ascended {
value[3] += value[2];
} else {
value[1] += value[3];
}
}
}
#[test]
fn noop_roundtrip() {
let value = [1, 3, 3, 7];
let cell = AliasedCell::new(Box::new(value));
let new_value = unsafe { *cell.into_inner() };
assert_eq!(new_value, [1, 3, 3, 7]);
}
#[test]
fn unused_alias() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
unsafe {
cell.alias_mut().as_mut();
}
let new_value = unsafe { *cell.into_inner() };
assert_eq!(new_value, [1, 3, 3, 7]);
}
#[test]
fn mutate() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
unsafe {
mutate_value(cell.alias_mut().as_mut());
}
let new_value = unsafe { *cell.into_inner() };
assert_eq!(new_value, [1, 10, 3, 7]);
}
#[test]
fn mutate_twice() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
unsafe {
mutate_value(cell.alias_mut().as_mut());
}
unsafe {
mutate_value(cell.alias_mut().as_mut());
}
let new_value = unsafe { *cell.into_inner() };
assert_eq!(new_value, [1, 17, 3, 7]);
}
#[test]
fn moves() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
unsafe {
mutate_value(cell.alias_mut().as_mut());
}
let mut cell2 = cell;
unsafe {
mutate_value(cell2.alias_mut().as_mut());
}
let cell3 = cell2;
let new_value = unsafe { *cell3.into_inner() };
assert_eq!(new_value, [1, 17, 3, 7]);
}
#[test]
fn mutate_deferred() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
let mut mutator = unsafe { Mutator::new(cell.alias_mut().as_mut()) };
unsafe {
mutator.mutate();
}
let new_value = unsafe { *cell.into_inner() };
assert_eq!(new_value, [1, 10, 3, 7]);
}
#[test]
fn mutate_deferred_twice() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
let mut mutator = unsafe { Mutator::new(cell.alias_mut().as_mut()) };
unsafe {
mutator.mutate();
}
unsafe {
mutator.mutate();
}
let new_value = unsafe { *cell.into_inner() };
assert_eq!(new_value, [1, 17, 3, 7]);
}
#[test]
fn deferred_moves() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
let mutator = unsafe { Mutator::new(cell.alias_mut().as_mut()) };
let cell2 = cell;
let mut mutator2 = mutator;
unsafe {
mutator2.mutate();
}
let cell3 = cell2;
let _mutator3 = mutator2;
let new_value = unsafe { *cell3.into_inner() };
assert_eq!(new_value, [1, 10, 3, 7]);
}
#[test]
fn safe_frobbing() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
let mut mutator = unsafe { Mutator::new(cell.alias_mut().as_mut()) };
unsafe {
mutator.mutate();
}
mutator.ascend();
unsafe {
mutator.mutate();
}
let new_value = unsafe { *cell.into_inner() };
assert_eq!(new_value, [1, 10, 3, 10]);
}
#[test]
fn two_mutators() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
let mut mutator1 = unsafe { Mutator::new(cell.alias_mut().as_mut()) };
unsafe {
mutator1.mutate();
}
let mut mutator2 = unsafe { Mutator::new(cell.alias_mut().as_mut()) };
unsafe {
mutator2.mutate();
}
let new_value = unsafe { *cell.into_inner() };
assert_eq!(new_value, [1, 17, 3, 7]);
}
#[test]
#[should_panic(
expected = "Trying to drop an AliasedCell, which may still have aliases outstanding."
)]
fn invalid_drop() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
unsafe {
let mut mutator = Mutator::new(cell.alias_mut().as_mut());
mutator.mutate();
drop(cell);
}
}
#[test]
#[should_panic(expected = "bye!")]
fn panic() {
let value = [1, 3, 3, 7];
let mut cell = AliasedCell::new(Box::new(value));
unsafe {
let mut mutator = Mutator::new(cell.alias_mut().as_mut());
mutator.mutate();
panic!("bye!");
}
}
}