Я думал о том, почему внутренняя изменчивость в Rust в большинстве случаев требует проверки во время выполнения (RefCell), и похоже, что я нашел безопасную альтернативу без затрат времени выполнения. Я назвал тип SafeCell (в основном потому, что это безопасная оболочка для UnsafeCell), и он в основном позволяет применять любую функцию к переносимому значению без риска перехода по ссылке:
struct SafeCell<T> {
inner: UnsafeCell<T>,
}
impl<T> SafeCell<T> {
pub fn new(value: T) -> Self {
Self { inner: UnsafeCell::new(value) }
}
pub fn apply<R, F>(&self, fun: F) -> R
where F: FnOnce(&mut T) -> R
{
// Reference below has a lifetime of the current scope, so if
// user tries to save it somewhere, borrow checker will catch this.
let reference: &mut T = unsafe { &mut *self.inner.get() };
fun(reference)
}
}
Этот тип может использоваться для внутренней изменчивости, как это:
pub struct MySet {
set: HashSet<i32>,
unique_lookups: SafeCell<HashSet<i32>>,
}
impl MySet {
// ...
pub fn contains(&self, value: i32) -> bool {
self.unique_lookups.apply(|lookups| lookups.insert(value));
self.set.contains(value)
}
pub fn unique_lookups_count(&self) -> usize {
self.unique_lookups.apply(|lookups| lookups.len())
}
}
Или в сочетании с R c:
fn foo(rc: Rc<SafeCell<String>>) {
rc.apply(|string| {
if string.starts_with("hello") {
string.push_str(", world!")
}
println!("{}", string);
});
}
Детская площадка.
Итак, мне было интересно:
- Есть ли какие-либо проблемы с безопасностью / надежностью этого типа?
- Если нет, то почему такой тип не является стандартным способом достижения внутренней изменчивости ? Похоже, что он так же удобен, как и RefCell, предоставляя stati c проверки времени жизни в отличие от проверок времени выполнения.