Тип RefCell<T>, аналогично
Cell<T>, тоже контейнер на одно значение произвольного типа. Но к самому значению нельзя достучаться прямо, а надо сначала получить ссылку на него методами borrow() или borrow_mut().
Метод borrow() даёт
константную ссылку. Значение можно получить методом get(), но нельзя изменить. Таких константных ссылок можно насоздавать сколько угодно - при условии, что не существует мутабельной ссылки.
Метод borrow_mut() даёт
мутабельную ссылку. Значение можно изменить методом set(). Но мутабельную ссылку можно получить только одну-единственную, и при отсутствии константных ссылок.
Question: Please show me implementation of type RefCell<T> in C++.Let’s tackle implementing Rust’s RefCell<T> in C++. This is an exciting challenge because RefCell<T> takes interior mutability a step further than Cell<T>. While Cell<T> allows mutation through a shared reference with a simple copy or swap, RefCell<T> provides borrowed access—either immutable (shared) or mutable (exclusive)—with runtime checks to enforce Rust’s borrowing rules: you can have many immutable borrows or one mutable borrow, but never both at once. In Rust, this is all about safety without locks, and we’ll aim to replicate that dynamic enforcement in C++.
In C++, we don’t have a borrow checker, so we’ll use a runtime mechanism to track borrows, similar to how RefCell uses a counter in Rust. The implementation will involve:
- A container for the value.
- Counters for active immutable and mutable borrows.
- Methods to borrow the value immutably (borrow) or mutably (borrow_mut), returning smart pointers that enforce the borrowing rules.
- Runtime checks to panic (or throw, in C++ terms) if the rules are violated.
Here’s the implementation:
refcell.cpp.
#include <iostream>
#include <memory>
#include <stdexcept>
#include <utility>
template <typename T>
class RefCell {
private:
T value;
mutable size_t borrow_count; // Tracks immutable borrows
mutable bool borrowed_mut; // Tracks if there's a mutable borrow
public:
explicit RefCell(T initial) : value(std::move(initial)), borrow_count(0), borrowed_mut(false) {}
class Ref {
private:
const RefCell<T>* cell;
friend class RefCell<T>;
Ref(const RefCell<T>* c) : cell(c) {
if (cell->borrowed_mut) {
throw std::runtime_error("Already mutably borrowed");
}
cell->borrow_count++;
}
public:
~Ref() {
if (cell) {
cell->borrow_count--;
}
}
Ref(const Ref&) = delete;
Ref& operator=(const Ref&) = delete;
Ref(Ref&& other) noexcept : cell(other.cell) { other.cell = nullptr; }
const T& get() const { return cell->value; }
};
class RefMut {
private:
RefCell<T>* cell;
friend class RefCell<T>;
RefMut(RefCell<T>* c) : cell(c) {
if (c->borrow_count > 0 || c->borrowed_mut) {
throw std::runtime_error("Already borrowed");
}
c->borrowed_mut = true;
}
public:
~RefMut() {
if (cell) {
cell->borrowed_mut = false;
}
}
RefMut(const RefMut&) = delete;
RefMut& operator=(const RefMut&) = delete;
RefMut(RefMut&& other) noexcept : cell(other.cell) { other.cell = nullptr; }
T& get() { return cell->value; }
void set(T new_value) {
cell->value = std::move(new_value);
}
};
Ref borrow() const { return Ref(this); }
RefMut borrow_mut() { return RefMut(this); }
};
( объяснения )