4 ms·
I'm actually working on a project I'm quite serious about but jokingly refer to as "Ergonomic Rust", which would make all of it a weird expression in Rust. It'
by benreesman 1y ago
I'm actually working on a project I'm quite serious about but jokingly refer to as "Ergonomic Rust", which would make all of it a weird expression in Rust.
It's a C++23 library suite and lint set that eliminates:
- UB in all but the most contrived cases (I think I can get it to zero with a modest clang patch set)
- bounds errors (see below)
- bans all naked pointers and most references of any kind (NVRO and elision are mandated since 17, and on modern hardware like `znver5` you're usually pessimizing with e.g. `const foo_t& foo`)
- and has no `usafe` keyword to fall back on, that's enforced at the conceptual module level by having things declare they are unsafe in their entirety via `extern "C"`
This stuff is really unlocked by C++23:
```
template<typename T>
concept SafeIndexable = requires(T& t, const T& ct, size_t idx) {
{ t.at(idx) } -> std::same_as<typename T::reference>;
{ ct.at(idx) } -> std::same_as<typename T::const_reference>;
// Banned: t[idx]
};
// Wrapper that forces .at() usage
template<SafeIndexable Container>
class Safe {
Container c;
public:
// Forward everything except operator[]
template<typename... Args>
Safe(Args&&... args) : c(std::forward<Args>(args)...) {}
// Evil genius move: operator[] calls .at()
auto operator[](size_t idx) -> decltype(auto) {
return c.at(idx); // Throws on bounds violation!
}
auto operator[](size_t idx) const -> decltype(auto) {
return c.at(idx);
}
// Forward other operations
auto begin() { return c.begin(); }
auto end() { return c.end(); }
// ... etc
};
// Usage:
Safe<std::vector<int>> vec{1, 2, 3};
vec[10]; // Throws std::out_of_range instead of UB!
```