Create a gist now

Instantly share code, notes, and snippets.

fn coerce(A) -> B using shared mutable state. Prints "hello world".
use either::{Either,Left,Right};
// SharedMutableState is an unsafe internal library used by both the ARC and the RWARC.
use private::{SharedMutableState, shared_mutable_state, clone_shared_mutable_state,
get_shared_mutable_state, get_shared_immutable_state};
type ARC<T: Send> = SharedMutableState<T>;
fn ARC<T: Send>(data: T) -> ARC<T> unsafe { shared_mutable_state(data) }
fn clone<T: Send>(x: &ARC<T>) -> ARC<T> unsafe { clone_shared_mutable_state(x) }
fn get<T: Send>(x: &a/ARC<T>) -> &a/T unsafe { get_shared_immutable_state(x) }
fn get_mut<T: Send>(x: &a/ARC<T>) -> &a/mut T unsafe { get_shared_mutable_state(x) }
fn coerce<A: Send, B>(data: A) -> B {
let data = ~mut Some(data);
let getA = fn~() -> A { option::swap_unwrap(data) };
let getB = fn~() -> B { fail };
// Step 0: Start with an "undefined" lazy value for B.
let arc: ARC<Either<fn~() -> A, fn~() -> B>> = ARC(Right(getB));
// We could well clone the ARC and send one handle to another task, but
// this way is simpler still.
let e1 = get(&arc);
let e2 = get_mut(&arc);
// Step 1: Alias the B. Ordinarily, the borrow-checker would "freeze"
// other mutable references to the same data. But with shared state,
// it can't know.
match *e1 {
Left(_) => fail,
Right(ref getB_alias) => {
// Step 2 (the "data race"): A pretends to be B.
*e2 = Left(getA);
// (the device has been modified.)
(*getB_alias)()
}
}
}
fn main() {
let x: ~str = coerce(~[104,101,108,108,111,32,119,111,114,108,100,0]);
io::println(x);
}
@bblum
Owner
bblum commented Sep 25, 2012

About the use of option::swap_unwrap in getA: I use this because Rust currently doesn't allow moving out of captured noncopyable variables in closures -- if the closure were to be called more than once, it would duplicate the noncopyable. (swap_unwrap dynamically enforces that it can't be called more than once.)

We've been planning to add a notion of "oneshot closures" to the language, which get consumed when they are called, to support this idiom (which actually turns out to be pretty common). With oneshot closures, the code would look something like let getA = once fn~(move data) -> A { return data }.

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