Get AI summaries of any video or article — Sign up free
C++ Is Getting A Borrow Checker thumbnail

C++ Is Getting A Borrow Checker

The PrimeTime·
5 min read

Based on The PrimeTime's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Safe C++ is an opt-in, feature-flagged extension of C++ that aims to enforce memory and thread safety without breaking existing codebases.

Briefing

C++ is moving toward a “safe subset” model—aiming to deliver Rust-like memory safety without abandoning C++’s existing ecosystem. A new proposal for “safe C++” would prohibit undefined behavior inside explicitly marked safe regions, require developers to opt out into unsafe code for operations that can’t be proven safe, and introduce a borrow-checking workflow similar to Rust’s. The practical goal is to let organizations keep writing C++ while reducing the most dangerous classes of bugs: use-after-free, uninitialized reads, and certain data-race hazards.

The proposal’s core mechanism is a feature-flagged extension of C++ that remains backward compatible: existing code should still compile normally, and safety checks only activate when developers choose the new model. Under the hood, the compiler lowers safe functions into an intermediate representation and then performs borrow checking on that form. A new `safe` modifier marks safe scopes, while an `unsafe` keyword gates pointer operations and other operations that can violate the safety guarantees. In effect, old C++ code is treated as unsafe by default, so developers would wrap unsafe operations inside clearly delimited blocks rather than mixing them invisibly throughout a program.

Safety is defined in categories. Lifetime safety targets uninitialized-variable bugs and use-after-free. Borrow checking is used to prevent invalid aliasing and to support a “destructive move”/relocation concept—once a value is moved, it can’t be used again. Type safety focuses on null-related hazards and other invalid states, with the proposal calling for safer alternatives such as choice/tagged union types when unions and raw pointers would otherwise be used. Runtime safety adds checks like bounds checking (for example, preventing out-of-range indexing) and other inserted protections, with the option to skip some checks in performance-critical paths.

Thread safety is also part of the plan, borrowing the Rust framing: types would be marked as safe to send across threads (`Send`) and safe to share between threads (`Sync`). The proposal suggests using concepts/constraints and interface-like queries to express these properties, so the compiler can reject unsafe cross-thread usage at compile time.

A major adoption question hangs over the proposal: rewriting existing C++ codebases is expensive, and Rust’s semantics differ enough from C++ that a full migration would require substantial developer retraining and code restructuring. The safe C++ approach tries to avoid that by extending C++ rather than replacing it. Still, the transcript repeatedly flags friction points—especially around interoperability with existing C and C++ libraries, the overhead of safety checks, and the complexity of the eventual syntax for safe references/borrows.

Even with those uncertainties, the direction is clear: safe C++ aims to make “accidental memory corruption” harder by construction, while preserving the ability to drop into unsafe code when necessary. The result would be a hybrid language model—C++ where safety is opt-in and enforced by compiler analysis, not just developer discipline.

Cornell Notes

Safe C++ is a proposal to add an opt-in, Rust-like safety model to C++ without breaking existing code. It would introduce a `safe` scope and require developers to use `unsafe` blocks for operations that can’t be proven safe, with old C++ treated as unsafe by default. The compiler would perform borrow checking on lowered intermediate representations, and safety guarantees would be grouped into lifetime safety, type safety, runtime safety, and thread safety (via Rust-style `Send`/`Sync` concepts). The motivation is to reduce undefined behavior and memory bugs while avoiding a full rewrite into Rust, since Rust and C++ semantics differ and migration is costly. The biggest open issues are syntax complexity, interoperability with existing libraries, and performance/overhead tradeoffs.

What does “safe C++” try to change about how C++ code is written and compiled?

It adds an opt-in safety model under a feature flag. Existing C++ code should still compile normally, but developers can mark functions/scopes as `safe`. Inside those safe regions, undefined behavior is prohibited from originating, and the compiler performs checks (including borrow checking) after lowering safe code to an intermediate representation. Operations that violate the safety rules—especially pointer operations—must occur inside explicit `unsafe` blocks.

How does the proposal handle memory lifetime and move semantics?

Lifetime safety targets bugs like uninitialized variable use and use-after-free. Borrow checking is used to enforce correct aliasing. The proposal also introduces a “destructive move”/relocation idea so that after a move, the original variable can’t be used again—preventing common categories of invalid access that arise from stale references.

What replaces risky C++ features inside the safe subset?

The proposal calls for safe alternatives when unsafe features like unions and raw pointers would otherwise be used. Instead of unions, it points toward choice/tagged union-style types (e.g., something like `std::variant`/tagged unions) to prevent misinterpreting stored data. For pointer-related hazards, safe references/borrow types would be used, while raw pointer arithmetic and comparisons would be disallowed in safe code.

How does safe C++ address concurrency and data races?

Thread safety is framed using Rust’s `Send` and `Sync` model: a type is `Send` if it’s safe to move to another thread, and `Sync` if it’s safe to share between threads. The proposal suggests using constraints/concepts and interface-like queries to enforce these properties at compile time, so unsafe cross-thread usage is rejected before runtime.

Why not just rewrite everything in Rust instead of extending C++?

A full rewrite is expensive because C++ and Rust semantics differ significantly, affecting how code is structured. That would require major developer retraining and codebase restructuring. Safe C++ aims to reduce the most dangerous bug classes while letting teams keep their C++ code and gradually adopt safety by marking scopes as `safe` and isolating unsafe operations.

What are the practical concerns raised about adopting this approach?

The transcript highlights uncertainty about syntax complexity for safe references/borrows, potential overhead from safety checks, and the difficulty of interoperating with existing C/C++ libraries. It also notes that many organizations may not use the newest C++ standards, which could slow adoption of any feature that depends on newer compiler support.

Review Questions

  1. What compiler analysis steps are described for enforcing safety in safe C++ (and where does borrow checking fit)?
  2. How do `safe` and `unsafe` interact in the proposal, and what kinds of operations are expected to be restricted in safe code?
  3. Which four categories of safety are named, and what is the role of `Send`/`Sync` within thread safety?

Key Points

  1. 1

    Safe C++ is an opt-in, feature-flagged extension of C++ that aims to enforce memory and thread safety without breaking existing codebases.

  2. 2

    A `safe` modifier marks safe scopes, while an `unsafe` keyword explicitly contains operations that can violate safety rules; old C++ is treated as unsafe by default.

  3. 3

    The compiler would lower safe code to an intermediate representation and run borrow checking similar to Rust to prevent invalid aliasing and lifetime errors.

  4. 4

    Safety guarantees are organized into lifetime safety, type safety, runtime safety, and thread safety, with runtime checks like bounds checking and optional skipping for performance.

  5. 5

    Thread safety would use Rust-style `Send`/`Sync` properties expressed via constraints/concepts to reject unsafe cross-thread usage at compile time.

  6. 6

    The proposal includes safer alternatives to risky features (e.g., choice/tagged union types) when unions and raw pointer patterns would be disallowed in safe regions.

  7. 7

    Adoption hinges on interoperability with existing C/C++ libraries, the eventual ergonomics of the safe syntax, and the performance cost of inserted checks.

Highlights

Safe C++ would make undefined behavior harder by construction: safe regions forbid UB from originating there, and unsafe operations must be explicitly fenced off.
Borrow checking is central: safe functions are lowered to an intermediate representation and then checked, aiming to mirror Rust’s core safety mechanism.
Thread safety is planned around Rust’s `Send`/`Sync` model, using compile-time constraints to control what can cross or be shared between threads.
The proposal targets incremental adoption—keeping existing C++ code compiling while letting teams opt into safety rather than rewriting everything in Rust.

Topics

  • Safe C++ Proposal
  • Borrow Checking
  • Thread Safety
  • Rust Interop
  • Undefined Behavior

Mentioned

  • Justin Keys