Make NonZero usable as Const param is about something else but it got me thinking, what if we had a way to tell the compiler that if one field of a struct is zero then another field must also be zero? Or more generally, invariant relationships among fields?
(#[...] notation used just to illustrate, the syntax isn't the important part)
Now RawFatPointer has 2^sizeof(*const T) niches, and I'm sure there's plenty of other interesting things the compiler could do with this information.
EDIT: Come to think of it, this is exactly the same concept as a multi-column CHECK constraint in SQL, isn't it? Only the SQL design goal is trapping data integrity errors as early as possible, rather than creating optimization opportunities (though I'm sure SQL query planners do use CHECK constraints for optimization)
I think the most important question here is: how is this invariant enforced? Does writing an unacceptable value to the field panic? Is it unsafe to write to the field?
I was thinking "it's unsafe to write to the field, except in cases where the compiler can prove that the write cannot possibly break the invariant", kinda like how NonNull works today. The challenge is of course pinning down how powerful we document the compiler's proofs to be.
I feel like "the compiler automatically inserts a dbg_assert! before each assignment" would also be useful, but I'd want that to be an opt-in mode. You need to know when you're electing to have assignments possibly panic.
struct CacheArcCount<T> {
// SAFETY: This `Arc`'s `ref_count` must equal the
// value of the `ref_count` field.
unsafe arc: Arc<T>,
// SAFETY: See [`CacheArcCount::arc`].
unsafe ref_count: usize,
}
The try_update callback gets a mutable reference to a copy of the object, and can do whatever it wants to its fields. On exit, the invariants are all checked. If all are good, the changes are copied back to the original object and try_update returns Ok, otherwise the changes are discarded and it returns Err.
This could probably be prototyped in a library with some derive macros.
(Unsolved problem: what do you do if the object is too big to copy twice for each update, or has uncopyable fields?)
There’s also an ongoing design effort to add contract programming to Rust, including invariant annotations, but I don’t know what its status is.
Aside: C++26 Contracts
In an unprecedented turn of events, the C++26 DIS (Draft International Standard, the final draft that was to become the C++26 IS modulo editorial fixes) was just rejected by the ISO National Bodies in a vote that was supposed to be largely a formality. The reason, according to the opponents, is the Contracts feature which hasn’t exactly lacked in the controversy department over its many years of design effort. Luckily Rust is in many ways a better fit for contracts, design-wise.