Skip to content

Instantly share code, notes, and snippets.

@msullivan
Last active February 3, 2020 19:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msullivan/6324973 to your computer and use it in GitHub Desktop.
Save msullivan/6324973 to your computer and use it in GitHub Desktop.
Universal type in rust
// Safe implementation based on the existential power of closures.
mod ClosureUniversal {
// A value of universal type is a pair of functions. store will
// write the underlying value into the associated tag, while clear
// will erase the data in the tag to prevent space leaks.
pub struct Univ {
priv store: @fn(),
priv clear: @fn()
}
// A tag is a mutable option used as a scratch space to write
// into when inspecting a tag.
pub struct Tag<A> {
priv r: @mut Option<A>
}
pub fn new_tag<A:'static>() -> Tag<A> {
Tag { r: @mut None }
}
pub fn inject<A:Clone+'static>(tag: Tag<A>, x: A) -> Univ {
Univ {
store: || *tag.r = Some(x.clone()),
clear: || *tag.r = None
}
}
pub fn project<A:Clone+'static>(tag: Tag<A>, x: Univ) -> Option<A> {
// Cause the value to be written into its tag. If the universal
// value was injected with our tag, then it will be in tag.r.
(x.store)();
// Read out the value.
let res = (*tag.r).clone();
// Clear the value, to prevent space leaks.
(x.clear)();
res
}
}
// Implementation based on explicit tags and unsafe casting.
mod UnsafeUniversal {
use std::managed;
use std::cast;
// We use pointers as our tags, since they are easy to generate
// uniquely and compare for equality. Uniquely generated integers
// might be better, but this is really simple and kind of cute.
type InnerTag = @();
fn new_inner_tag() -> InnerTag { @() }
fn tag_eq(x: InnerTag, y: InnerTag) -> bool {
managed::ptr_eq(x, y)
}
pub struct Univ {
tag: InnerTag,
value: @()
}
pub struct Tag<A> {
priv inner: InnerTag
}
pub fn new_tag<A:'static>() -> Tag<A> {
Tag { inner: new_inner_tag() }
}
pub fn inject<A:Clone+'static>(tag: Tag<A>, x: A) -> Univ {
Univ {
tag: tag.inner,
value: unsafe { cast::transmute(@(x.clone())) }
}
}
pub fn project<A:Clone+'static>(tag: Tag<A>, x: Univ) -> Option<A> {
if tag_eq(tag.inner, x.tag) {
let ptr: @A = unsafe { cast::transmute(x.value) };
Some((*ptr).clone())
} else {
None
}
}
}
fn main() {
// use ClosureUniversal::*;
use UnsafeUniversal::*;
// Create some tags
let int_tag = new_tag::<int>();
let str_tag = new_tag::<~str>();
// Create some universal values with those tags
let u1 = inject(int_tag, 5);
let u2 = inject(int_tag, 6);
let u3 = inject(str_tag, ~"hello, world");
// Try reading them
println(fmt!("%?", project(int_tag, u1))); // Some(5)
println(fmt!("%?", project(int_tag, u2))); // Some(6)
println(fmt!("%?", project(int_tag, u3))); // None
println(fmt!("%?", project(str_tag, u1))); // None
println(fmt!("%?", project(str_tag, u2))); // None
println(fmt!("%?", project(str_tag, u3))); // Some(~"hello, world")
// Try out a *different* int tag.
let int_tag2 = new_tag::<int>();
// It can not be used to read things created by the other int tag
println(fmt!("%?", project(int_tag2, u1))); // None
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment