...
let len = s.len();
let ptr = {
let alloc_len = len
.checked_add(USIZE_SIZE)
.expect("length overflow");
let layout = std::alloc::Layout::array::<u8>(alloc_len)
.unwrap()
.align_to(USIZE_ALIGN)
.expect("invalid layout");
self.mem.alloc_layout(layout)
};
let s = unsafe {
ptr.cast::<usize>().write(len);
ptr.byte_add(USIZE_SIZE)
.as_ptr()
.copy_from_nonoverlapping(s.as_ptr(), len);
InternedStr::from_ptr(ptr)
};
...
Bump возвращает указатель на только что выделенную память и потому память под ним никак не может пересекаться с переданной аргументом строкой, поэтому мы можем безопасно воспользоваться copy_from_nonoverlappingТеперь осталось лишь сохранить вновь созданную строку и вернуть её:
...
strs.insert(unsafe { s.reborrow_unchecked() });
s
}
reborrow_unchecked тут — это метод для принудительной смены времени жизни InternedStr:impl InternedStr<'_> {
unsafe fn reborrow_unchecked<'b>(self) -> InternedStr<'b> {
InternedStr {
ptr: self.ptr,
_lt: PhantomData,
}
}
}Как и обещано, безопасность обеспечена API: метод для выделения строки заимствует
Interner, поэтому использовать возвращённые строки после дропа Interner нельзя.Как же решается переиспользование памяти? Через делегацию
Bump, главным образом:impl Interner {
pub fn clear(&mut self) {
self.strs.get_mut().clear();
self.mem.reset();
}
}Из-за уникального заимствования вызов этого метода инвалидирует все ранее выделенные строки, поэтому обратиться к интернированной строке после очищения пула строк нельзя.
И ещё пара моментов. Использование
strs.get(s), где s имеет тип &str, а в множестве хранятся значения другого типа InternedStr, возможно только при условии, что InternedStr "похож" на str — или, иными словами, реализовывает Borrow<str>. Реализация делается элементарно через делегацию методу as_str. Также InternedStr, очевидно, нужно уметь хэшировать, и хэш должен считаться так же, как и для соответствующей строки. По этой причине #[derive(Hash)] не подойдёт и нужно писать его реализацию руками:impl Hash for InternedStr<'_> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}