-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: Using with complex types like HashMap #83
Comments
The thing that You can use an interior-mutable type as use std::collections::HashMap;
use typed_arena::Arena;
#[ouroboros::self_referencing]
struct StringIndex {
store: Arena<String>,
#[borrows(store)]
#[covariant]
index: HashMap<&'this str, usize>,
}
impl StringIndex {
pub fn get_or_insert(&mut self, key: &str) -> usize {
self.with_mut(|fields| -> usize {
if let Some(&value) = fields.index.get(key) {
value
} else {
let idx = fields.store.len();
let stored_str: &str = fields.store.alloc(key.to_owned());
fields.index.insert(stored_str, idx);
idx
}
})
}
} However, given what you've written I imagine you also want to be able to get from use std::collections::HashMap;
use typed_arena::Arena;
#[ouroboros::self_referencing]
struct StringIndex {
store: Arena<String>,
#[borrows(store)]
#[covariant]
index: HashMap<&'this str, usize>,
#[borrows(store)]
#[covariant]
rev_index: Vec<&'this str>,
}
impl StringIndex {
pub fn get_or_insert(&mut self, key: &str) -> usize {
self.with_mut(|fields| -> usize {
if let Some(&value) = fields.index.get(key) {
value
} else {
let idx = fields.store.len();
let stored_str: &str = fields.store.alloc(key.to_owned());
fields.rev_index.push(stored_str);
fields.index.insert(stored_str, idx);
idx
}
})
}
pub fn lookup_idx(&self, idx: usize) -> Option<&str> {
self.borrow_rev_index().get(idx).copied()
}
} |
@kpreid thank you for such an awesome write up and explanation!!! TIL :) You are absolutely correct -- my use case is an ever-growing (accumulator style) vector -- the user needs to build a vector of values (using // Note that this interface should support `&'static str`, `String`, or any other `T`s,
// but not a mix of them in the same container.
fn append<T>(&mut self, value: T) -> usize;
fn into(self) -> Vec<T>; From the sounds of it, I may have to actually implement this as a separate crate with unsafe functionality - if the Vec is only growing, the non-exposed references to the vector's values shouldn't violate any Rust memory rules. So internally it would be a P.S. Possible optimization of the above is if the |
In this case, pub fn into_vec(self) -> Vec<String> {
self.into_heads().store.into_vec()
}
No, that's not reliably true: when the |
thanks, when i tried, i realized that the ref was into Vec, not into the memory allocated for the string itself. I came up with this (without Arena): I simply duplicate the mod stores {
use std::collections::HashMap;
use std::mem;
#[derive(Default)]
struct LeakyKeyHashMap<K, V>(pub HashMap<K, V>);
impl<K, V> Drop for LeakyKeyHashMap<K, V> {
fn drop(&mut self) {
for k in mem::take(&mut self.0).into_keys() {
mem::forget(k);
}
}
}
#[derive(Default)]
pub struct Store<T> {
keys: Vec<T>,
index: LeakyKeyHashMap<T, usize>,
}
impl<T> Store<T> {
pub fn into_vec(self) -> Vec<T> {
self.keys
}
}
impl Store<String> {
pub fn insert_or_get(&mut self, key: String) -> usize {
if let Some(index) = self.index.0.get(&key) {
*index
} else {
let index = self.keys.len();
let (ptr, len, cap) = (key.as_ptr(), key.len(), key.capacity());
self.keys.push(key);
let key_dup = unsafe { String::from_raw_parts(ptr as *mut u8, len, cap) };
self.index.0.insert(key_dup, index);
index
}
}
}
impl Store<&'static str> {
pub fn insert_or_get(&mut self, key: &'static str) -> usize {
if let Some(index) = self.index.0.get(&key) {
*index
} else {
let index = self.keys.len();
self.keys.push(key);
self.index.0.insert(key, index);
index
}
}
}
}
fn main() {
use stores::Store;
let mut store = Store::default();
let foo_idx = Store::<String>::insert_or_get(&mut store, "foo".to_string());
Store::<String>::insert_or_get(&mut store, "bar".to_string());
let foo_idx2 = Store::<String>::insert_or_get(&mut store, "foo".to_string());
assert_eq!(foo_idx, foo_idx2);
let keys = store.into_vec();
assert_eq!(keys, vec!["foo".to_string(), "bar".to_string()]);
} |
I made a new crate with the above idea, hope it is "sound" :) https://github.com/nyurik/dup-indexer |
I would like to build this type of a struct, just like I outlined in stackoverflow question. It seems Ouroboros is the closest to this, but I am still not certain if it's possible:
and implement some magical method to perform "lookup or push":
The text was updated successfully, but these errors were encountered: