惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
月光博客
月光博客
博客园_首页
博客园 - 叶小钗
T
Tailwind CSS Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
InfoQ
量子位
小众软件
小众软件
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
IT之家
IT之家
Jina AI
Jina AI
阮一峰的网络日志
阮一峰的网络日志
G
Google Developers Blog
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks
云风的 BLOG
云风的 BLOG
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报

LWN.net comments

tcmalloc's weird hack [LWN.net] Fixed? [LWN.net] mpd [LWN.net] Userspace AX.25 [LWN.net] RIP [LWN.net] My two cents... [LWN.net] pipx [LWN.net] Tragedy [LWN.net] A young man destined for glory [LWN.net] And 'less' won't let you search [LWN.net] A great loss [LWN.net] Sad and shocking news [LWN.net] Easy migration from Clementine [LWN.net] Sad coincidence [LWN.net] GNOME is actually usable thanks to Seth et al [LWN.net] Sad news :( [LWN.net] armhf supports preempt_rt [LWN.net] MusicBrainz accurracy [LWN.net] On open source maintainership [LWN.net] Let's stop here [LWN.net] Not a new thing [LWN.net] uv is indeed great pgmoneta Some comments on this on a Postgres blog feed [LWN.net] uv [LWN.net] going to Debian [LWN.net] Upgrading 64-bit-capable systems to 64-bit kernels? [LWN.net] Free Software foundations Maintainers can wait for code review but not for publish review? A reasonably extreme point of view [LWN.net]
C++ 26 contracts [LWN.net]
NYKevin · 2026-05-12 · via LWN.net comments

I do like type invariants, but sometimes a contract facility has better ergonomics. If I can't have both I guess I'll take type invariants, but I would prefer both.

C++ 26 contracts

Posted May 12, 2026 17:32 UTC (Tue) by NYKevin (subscriber, #129325) [Link]

One clever (but unfortunately Rust-only) pattern I have discovered is the static enum. It goes like this:

// TODO: Replace Infallible with ! when the latter is stabilized.
use std::convert::Infallible;

pub trait Variant: Sized {
    // Type bounds added for ergonomics, not essential.
    type OnlyLeft: Copy + 'static;
    type OnlyRight: Copy + 'static;
    fn into_left<T, U>(e: Either<Self, T, U>, x: Self::OnlyLeft) -> T;
    fn into_right<T, U>(e: Either<Self, T, U>, x: Self::OnlyRight) -> U;
    fn select<T, U>(t: T, u: U) -> Either<Self, T, U>;
}

pub enum Either<V: Variant, T, U>{
    Left(T, V::OnlyLeft),
    Right(U, V::OnlyRight),
}

pub struct LeftVariant;
pub struct RightVariant;

impl Variant for LeftVariant {
    type OnlyLeft = ();
    type OnlyRight = Infallible;
    fn into_left<T, U>(e: Either<Self, T, U>, _: Self::OnlyLeft) -> T{
        let Either::<Self, T, U>::Left(v, _) = e;
        v
    }
    // TODO: When replacing Infallible with !, we can simplify this to just return x directly.
    fn into_right<T, U>(_: Either<Self, T, U>, x: Self::OnlyRight) -> U{
        match x {}
    }
    fn select<T, U>(t: T, _: U) -> Either<Self, T, U>{
        Either::Left(t, ())
    }
}

impl Variant for RightVariant {
    type OnlyLeft = Infallible;
    type OnlyRight = ();
    fn into_left<T, U>(_: Either<Self, T, U>, x: Self::OnlyLeft) -> T{
        match x {}
    }
    fn into_right<T, U>(e: Either<Self, T, U>, _: Self::OnlyRight) -> U{
        let Either::<Self, T, U>::Right(v, _) = e;
        v
    }
    fn select<T, U>(_: T, u: U) -> Either<Self, T, U>{
        Either::Right(u, ())
    }
}

// Need a macro so that we only evaluate one or the other of t and u. They can both e.g. move
// from the same places, have overlapping mutable borrows, etc.
// XXX: It is somewhat ugly to take a type as an argument. Better syntax would look like this:
// new_either!<V>(t, u).
// But macro_rules! cannot consume that syntax, and proc macros are overkill. Oh well.
macro_rules! new_either{
    ($v:ty, $t:expr, $u:expr) => {
        match <$v as $crate::Variant>::select((), ()) {
            $crate::Either::Left(_, x) => $crate::Either::Left($t, x),
            $crate::Either::Right(_, x) => $crate::Either::Right($u, x),
        }
    }
}

// Either should provide various methods for manipulating its contents, not shown here.
// It should also blanket impl some std traits when T and U impl those traits.

The point is that destructuring an Either instance inside of a match expression will only emit code for the branch that is actually taken. This means that we can write simple and obvious code that looks like a runtime branch, but is actually resolved at compile time. For example, new_either!() is a zero-overhead (branchless) operation despite containing a match with two arms. After monomorphization, one of those arms will always materialize an empty type (as the value of x), and the compiler will regard that as conclusive evidence that the arm is unreachable (or UB).

It also means we can pass around these OnlyLeft and OnlyRight tokens freely to prove which variant is active, and later use the into_left() and into_right() functions as needed. Since the tokens are either ZSTs or empty types, they never emit any code whatsoever.

I suspect, but cannot easily prove, that most or all design-by-contract use cases can be reformulated in terms of ZSTs, empty types, and similar constructs to what I show above. But retrofitting this much type system complexity into C++ is probably not a good idea.