Skip to content

Instantly share code, notes, and snippets.

@orenbenkiki
Created August 11, 2013 20:39
Show Gist options
  • Save orenbenkiki/6206763 to your computer and use it in GitHub Desktop.
Save orenbenkiki/6206763 to your computer and use it in GitHub Desktop.
src/anthill/intern.rs:71:24: 73:25 error: instantiating a type parameter with an incompatible type `intern::intern::Interner`, which does not fulfill `Send+Freeze`
src/anthill/intern.rs:71 do arc_interner.read |interner| {
src/anthill/intern.rs:72 interner.id_to_str[self.id].clone()
src/anthill/intern.rs:73 }
src/anthill/intern.rs:88:20: 108:21 error: instantiating a type parameter with an incompatible type `intern::intern::Interner`, which does not fulfill `Send+Freeze`
src/anthill/intern.rs:88 do arc_interner.read |interner| {
src/anthill/intern.rs:89 match interner.str_to_intern.find(&burrowed.to_str()) {
src/anthill/intern.rs:90 Some(intern) => *intern,
src/anthill/intern.rs:91 None => {
src/anthill/intern.rs:92 // TRICKY: If we didn't find it above, we need
src/anthill/intern.rs:93 // to get a write lock and re-find it, to
...
src/anthill/intern.rs:99:32: 105:33 error: instantiating a type parameter with an incompatible type `intern::intern::Interner`, which does not fulfill `Send+Freeze`
src/anthill/intern.rs:99 do arc_interner.write |mut_interner: &mut Interner| {
src/anthill/intern.rs:100 let intern = do mut_interner.str_to_intern.find_or_insert_with(burrowed.to_str()) |owned| {
src/anthill/intern.rs:101 mut_interner.id_to_str.push(owned.clone());
src/anthill/intern.rs:102 Intern { id: mut_interner.id_to_str.len() - 1 }
src/anthill/intern.rs:103 };
src/anthill/intern.rs:104 *intern
...
src/anthill/intern.rs:118:36: 118:46 error: instantiating a type parameter with an incompatible type `intern::intern::Interner`, which does not fulfill `Send+Freeze`
src/anthill/intern.rs:118 set_task_local_arc_interner(RWArc::new(Interner::new()));
/// Interns are an efficient representation of a string (also known as
/// "interned strings", "atoms", "symbols", ...). They allow efficient
/// comparison for equality, use as keys, etc.
///
/// The implementation in this module relies on the program to create a global
/// interner and pass it to all the tasks. This ensures interns created
/// anywhere in the program will be compatible (like the Erlang atom
/// mechanism). TODO: It would be nice to do this in a safer way without
/// requiring the program's cooperation.
mod intern {
use extra::arc::RWArc;
use std::hashmap::HashMap;
use std::local_data;
static KEY_ARC_INTERNER: local_data::Key<RWArc<Interner>> = &local_data::Key;
/// Set the task local ARC for the global interner. This assumes "someone"
/// creates a single global interner and hands it off to each task. Once
/// this is done, interns created anywhere will be compatible with each
/// other. If this isn't done, "bad things will happen".
pub fn set_task_local_arc_interner(arc_interner: RWArc<Interner>) {
local_data::set(KEY_ARC_INTERNER, arc_interner)
}
/// An interner holds all the known strings, ensuring re-interning the same
/// string will always return the same intern.
struct Interner {
str_to_intern: @mut HashMap<~str, Intern>,
id_to_str: @mut ~[~str],
}
impl Interner {
/// Create a new empty interner. TODO: This should really be a
/// singleton (though we do create instances in tests...).
pub fn new() -> Interner {
Interner {
str_to_intern: @mut HashMap::new(),
id_to_str: @mut ~[]
}
}
}
/// An intern is an efficient representation of a string, allowing
/// efficient comparison for equality, use as keys, etc.
struct Intern {
id: uint,
}
impl TotalEq for Intern {
fn equals(&self, other: &Intern) -> bool {
self.id == other.id
}
}
impl Eq for Intern {
fn eq(&self, other: &Intern) -> bool {
self.id == other.id
}
fn ne(&self, other: &Intern) -> bool {
self.id != other.id
}
}
impl ToStr for Intern {
fn to_str(&self) -> ~str {
do local_data::get(KEY_ARC_INTERNER) |maybe_arc_interner| {
match maybe_arc_interner {
None => fail!("no interner set for task"),
Some(arc_interner) => {
do arc_interner.read |interner| {
interner.id_to_str[self.id].clone()
}
}
}
}
}
}
/// Intern a string using the task local interner.
pub fn intern(burrowed: &str) -> Intern {
do local_data::get(KEY_ARC_INTERNER) |maybe_arc_interner| {
match maybe_arc_interner {
None => fail!("no task local ARC for global interner"),
Some(arc_interner) => {
// TRICKY: Only obtain a read lock for the common case
// where the string was previously interned.
do arc_interner.read |interner| {
match interner.str_to_intern.find(&burrowed.to_str()) {
Some(intern) => *intern,
None => {
// TRICKY: If we didn't find it above, we need
// to get a write lock and re-find it, to
// ensure no other task sneaked in and added it
// between the above find and obtaining the
// write lock below. This sort of race
// condition is why one shouldn't write code
// using locks unless it is *really* necessary.
do arc_interner.write |mut_interner: &mut Interner| {
let intern = do mut_interner.str_to_intern.find_or_insert_with(burrowed.to_str()) |owned| {
mut_interner.id_to_str.push(owned.clone());
Intern { id: mut_interner.id_to_str.len() - 1 }
};
*intern
}
}
}
}
}
}
}
}
#[test]
fn intern_strings() {
// TODO: This leaves the interner in the task local data after the test
// is run. Provide a "do with_test_interner { ... }" function?
set_task_local_arc_interner(RWArc::new(Interner::new()));
let foo = intern("foo");
let bar = intern("bar");
let baz = intern("foo");
assert!(foo != bar);
assert!(foo == baz);
assert!(foo.to_str() == ~"foo");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment