Тип Cell<T>
2025-03-01 13:56![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Продолжаю двигаться по списку: изучаю базовые примитивы языка Rust. Сегодня рассмотрим Cell<T>, RefCell<T> и, на десерт, Option<T>.
Cell это простой контейнер на одно значение произвольного типа. С одной хитрой фичей: если значение неизменяемое (то есть имеет атрибут const), Cell позволяет обходить это ограничение и всё-таки изменять его. Аналогично ключевому слову "mutable" в Си++. Спросим у grok.com.
Question: Please show me how type Cell<T> can be implemented in C++.
Let’s dive into how we might implement Rust’s Cell<T> in C++. Rust’s Cell<T> is a fascinating type - it provides interior mutability, allowing you to mutate its contents even when you only have a shared reference, all while being safe in a single-threaded context. It’s lightweight, with no runtime overhead beyond the mutation itself, and it doesn’t involve locks since it’s not thread-safe (that’s what RefCell or atomics are for in Rust). In C++, we don’t have the exact same ownership and borrowing rules, so we’ll need to adapt the concept while preserving its spirit.
The core idea of Cell<T> is simple: it wraps a value of type T and provides methods to get and set that value, bypassing C++’s usual const-correctness restrictions in a controlled way. Rust uses it to allow mutation through shared references, so in C++, we’ll aim for something that lets us modify a value even when we might otherwise be constrained, like through a const reference, while keeping the implementation straightforward.
Here’s a basic implementation in C++: cell.cpp.
Cell это простой контейнер на одно значение произвольного типа. С одной хитрой фичей: если значение неизменяемое (то есть имеет атрибут const), Cell позволяет обходить это ограничение и всё-таки изменять его. Аналогично ключевому слову "mutable" в Си++. Спросим у grok.com.
Question: Please show me how type Cell<T> can be implemented in C++.
Let’s dive into how we might implement Rust’s Cell<T> in C++. Rust’s Cell<T> is a fascinating type - it provides interior mutability, allowing you to mutate its contents even when you only have a shared reference, all while being safe in a single-threaded context. It’s lightweight, with no runtime overhead beyond the mutation itself, and it doesn’t involve locks since it’s not thread-safe (that’s what RefCell or atomics are for in Rust). In C++, we don’t have the exact same ownership and borrowing rules, so we’ll need to adapt the concept while preserving its spirit.
The core idea of Cell<T> is simple: it wraps a value of type T and provides methods to get and set that value, bypassing C++’s usual const-correctness restrictions in a controlled way. Rust uses it to allow mutation through shared references, so in C++, we’ll aim for something that lets us modify a value even when we might otherwise be constrained, like through a const reference, while keeping the implementation straightforward.
Here’s a basic implementation in C++: cell.cpp.
#include <utility>
#include <iostream>
template <typename T>
class Cell {
private:
mutable T value; // Mutable to allow mutation even when Cell is const
public:
// Constructor
explicit Cell(T initial) : value(std::move(initial)) {}
// Get a copy of the value
T get() const {
return value;
}
// Set a new value
void set(T new_value) const {
value = std::move(new_value);
}
// Replace the value and return the old one
T replace(T new_value) const {
T old_value = std::move(value);
value = std::move(new_value);
return old_value;
}
};
Notes
- Constness in C++: In Rust, Cell<T> shines because it subverts the immutability of shared references. In C++, if you make a Cell const, set and replace won’t work without some trickery. Rust’s Cell uses its internal implementation to allow mutation through a shared reference, but in C++, const objects are immutable unless you use mutable or const_cast. By making the value member mutable, we allow the Cell to modify its contents even when the Cell object itself is const, mimicking how Rust permits mutation through a shared reference.
- Using mutable here lets Cell behave more like Rust’s version, where the container’s constness doesn’t prevent internal mutation. This is closer to “interior mutability,” but it’s a bit of a cheat in C++ - use it carefully, as it might surprise other developers expecting const to mean full immutability.
- Move-Only Types: Rust’s Cell<T> works with any T, even non-copyable types, since get requires Copy but set and replace don’t. In C++, if T is move-only (e.g., std::unique_ptr), get() as written above won’t compile. You could add a std::enable_if to disable get() for non-copyable types or provide a separate get_ref() method returning const T&.
Example usage
Running this code will produce:int main() {
// Non-const Cell
Cell<int> cell(42);
std::cout << "Initial value: " << cell.get() << "\n"; // Prints 42
cell.set(100);
std::cout << "After set: " << cell.get() << "\n"; // Prints 100
int old = cell.replace(200);
std::cout << "Old value: " << old << "\n"; // Prints 100
std::cout << "New value: " << cell.get() << "\n"; // Prints 200
// Const Cell
const Cell<int> const_cell(300);
std::cout << "Const cell initial value: " << const_cell.get() << "\n"; // Prints 300
const_cell.set(400); // Works despite constness!
std::cout << "Const cell after set: " << const_cell.get() << "\n"; // Prints 400
int old_const = const_cell.replace(500);
std::cout << "Const cell old value: " << old_const << "\n"; // Prints 400
std::cout << "Const cell new value: " << const_cell.get() << "\n"; // Prints 500
return 0;
}
Initial value: 42
After set: 100
Old value: 100
New value: 200
Const cell initial value: 300
Const cell after set: 400
Const cell old value: 400
Const cell new value: 500
Why This Works
- The mutable keyword tells C++ that value can be modified even in a const context. This is the key to achieving “interior mutability” in C++, akin to how Rust’s Cell<T> bypasses reference immutability.
- std::move is still used for efficiency, ensuring we don’t unnecessarily copy objects when setting or replacing values.
- Like Rust’s Cell<T>, this isn’t thread-safe—use it in single-threaded scenarios or add synchronization if needed.
no subject
Date: 2025-03-01 22:51 (UTC)Cell ? Т.е. сonst у нас не сама cell, а её содержимое.
no subject
Date: 2025-03-01 23:27 (UTC)no subject
Date: 2025-03-02 01:00 (UTC)no subject
Date: 2025-03-02 01:10 (UTC)