{"schemaVersion":1,"id":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val","title":"Memory-Safe C++ Alternatives: Carbon, Cpp2, Val, and Circle Compared","description":"A technical comparison of the emerging C++ successor languages, Carbon, Cpp2/cppfront, Val, and Circle, with real interop examples, migration cost analysis, and objective assessments of current maturity.","language":"en","datePublished":"2025-10-18","dateModified":null,"contentFormat":"text/markdown","content":"Programming Languages\n\n# Memory-Safe C++ Alternatives: Carbon, Cpp2, Val, and Circle Compared\n\n2025-10-188 min read\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nAlso available in: [Español](<https://lucioduran.com/es/blog/memory-safe-c-alternatives-carbon-cpp2-val>)\n\n## The Problem That Persists\n\nC++ doesn't have a memory safety story. It has a \"discipline and tooling\" story. AddressSanitizer catches bugs at runtime. Static analyzers catch some at compile time. The C++ Core Guidelines are a style guide, not an enforcement mechanism. And after three decades of incremental fixes, CVEs rooted in use-after-free still ship regularly and buffer overflows.\n\nThe NSA's 2022 guidance, CISA's subsequent push, and now the 2025 EU Cyber Resilience Act requirements have made \"just be more careful\" an untenable position for anything touching regulated industries. Hence the need to evaluate actual alternatives.\n\n## Carbon: Google's Interop-First Bet\n\nCarbon's pitch is straightforward: be to C++ what Kotlin was to Java. Full bidirectional interop, gradual migration, and a modernized syntax that eliminates the most dangerous footguns.\n\n### Interop in Practice\n\nThe following example imports a C++ audio buffer class in [Carbon](<https://github.com/carbon-language/carbon-lang>):\n\n```carbon\nimport Cpp library \"audio/ring_buffer.h\";\n\nfn ProcessBlock(buf: Cpp.RingBuffer*, block_size: i32) -> bool {\n var samples: auto = buf->ReadBlock(block_size);\n if (samples.size() != block_size) {\n return false;\n }\n\n // Carbon's bounds-checked access\n var i: i32 = 0;\n while (i < block_size) {\n samples[i] = ApplyGain(samples[i], 0.7);\n ++i;\n }\n\n return buf->WriteBlock(samples);\n}\n```\n\nThe ` import Cpp ` directive is genuinely impressive when it works. Testing shows that an entire ring buffer implementation, including ` std::atomic ` fence operations, can be imported and called from Carbon without writing a single line of binding code. The compiler understands C++ name mangling, vtable layouts, and even ` std::unique_ptr ` semantics.\n\nWhere it falls apart: SIMD code. Codebases using GCC vector extensions and ` __attribute__((target(\"avx2\"))) ` extensively will find that Carbon's interop layer chokes on these. Every SIMD function needs to be wrapped in a plain C-linkage wrapper, which defeats the purpose of seamless interop.\n\n### Migration Cost Assessment\n\nFor a representative 180k-line codebase, the estimated breakdown:\n\n- **Clean modern C++17 code** (~60% of the codebase): ~2.5 engineer-months to port\n- **Template-heavy generic code** (~25%): ~6 engineer-months, with significant redesign\n- **SIMD/intrinsics/platform-specific** (~15%): Not feasible to port. Keep as C++ and interop.\n\nTotal realistic estimate: 8-10 engineer-months for an incomplete migration. That is the honest number rarely shared at conferences.\n\n## Cpp2/cppfront: Herb Sutter's Pragmatic Middle Ground\n\nCpp2 takes a fundamentally different approach. Instead of building a new language with interop, it defines a new syntax that transpiles to standard C++20. Your existing build system, your existing debugger, your existing profiler, they all just work because the output is C++.\n\n### The Syntax in Action\n\nThe same ring buffer processing logic in [Cpp2](<https://github.com/hsutter/cppfront>):\n\n```cpp2\nProcessBlock: (inout buf: RingBuffer, block_size: int) -> bool = {\n samples := buf.ReadBlock(block_size);\n if samples.size() != block_size {\n return false;\n }\n\n for (0 ..< block_size) do (i) {\n samples[i] = ApplyGain(samples[i], 0.7);\n }\n\n return buf.WriteBlock(samples);\n}\n```\n\nNotice the ` inout ` parameter, Cpp2 makes mutation explicit at the call site. Parameters are ` in ` (const ref) by default, which eliminates an entire class of accidental-mutation bugs.\n\n### What Actually Transpiles\n\nThe cppfront compiler turns that into roughly this C++20:\n\n```cpp\nauto ProcessBlock(RingBuffer& buf, int block_size) -> bool {\n auto samples = buf.ReadBlock(block_size);\n if (samples.size() != block_size) {\n return false;\n }\n\n for (auto i = 0; i < block_size; ++i) {\n samples[i] = ApplyGain(samples[i], 0.7);\n }\n\n return buf.WriteBlock(samples);\n}\n```\n\nThe key insight: cppfront adds initialization safety (no uninitialized variables), null safety (no null dereference on ` cpp2::unique_ptr `), bounds safety (checked subscript by default), and type safety (no implicit narrowing conversions). All enforced at the Cpp2 syntax level, all compiled away to zero-overhead C++20.\n\n### The Catch\n\nCppfront's error messages are… not great. When something goes wrong during transpilation, you get an error pointing to a line in your Cpp2 source, but if the error is in the generated C++, your compiler barfs on generated code you didn't write. A common frustration: spending an entire afternoon debugging a template instantiation failure that turns out to be a missing ` requires ` clause that cppfront should have generated but did not.\n\nAlso, IDE support is limited. There's a VS Code extension with basic syntax highlighting, but no reliable go-to-definition, no refactoring tools, and the language server crashes on files over ~500 lines. This matters more than people think.\n\n## Val: The Mutable Value Semantics Approach\n\n[Val](<https://www.val-lang.dev/>) (formerly known as Hylo) takes the most radical departure from C++. Its core premise: if every value is uniquely owned and mutations always happen in-place, you don't need a borrow checker, you don't need garbage collection, and you get memory safety with zero runtime overhead.\n\n### Ownership Without Borrowing\n\n```val\nfun processBlock(buf: inout RingBuffer, blockSize: Int) -> Bool {\n var samples = buf.readBlock(blockSize)\n if samples.count != blockSize {\n return false\n }\n\n for i in 0 ..< blockSize {\n &samples[i] = applyGain(samples[i], 0.7)\n }\n\n return buf.writeBlock(samples)\n}\n```\n\nThe ` & ` prefix on ` samples[i] ` is Val's way of saying \"I'm mutating this in-place.\" If you forget it, the compiler refuses to compile. It's explicit, it's visible, and it eliminates the \"did I accidentally copy or alias this?\" class of bugs.\n\nWhat makes Val genuinely interesting compared to Rust is the absence of lifetime annotations. There are no ` 'a ` lifetimes sprinkled through your function signatures. Val's type system ensures safety through value semantics alone, if you have a value, you own it. Period. No shared mutable state, no data races by construction.\n\n### Current Limitations of Val\n\nVal is the youngest of the bunch, and it shows. The compiler (built on Swift's SIL) is slow, compiling a 3000-line test module took 47 seconds versus 3 seconds in Cpp2 and 8 seconds in Carbon. The standard library is minimal. There's no FFI story worth mentioning, you can call C functions through a ` @foreign ` annotation, but C++ interop doesn't exist yet.\n\nFor a greenfield project with full stack control? Val's design is arguably the most elegant. For migrating an existing C++ codebase? It's a non-starter in 2026.\n\n## Circle: Sean Baxter's One-Man Revolution\n\n[Circle](<https://www.circle-lang.org/>) is the wildcard. Sean Baxter has single-handedly built a C++ compiler that extends the language with first-class safety features, pattern matching, reflection, and what amounts to a new language that happens to be a strict superset of C++.\n\n### Safety Annotations in Practice\n\n```cpp\n#feature on safety\n\nvoid ProcessBlock(RingBuffer& buf, int block_size) safe {\n auto samples = buf.ReadBlock(block_size); // returns std2::vector\n if (samples.size() != block_size) {\n return;\n }\n\n for (int i = 0; i < block_size; ++i) {\n samples[i] = ApplyGain(samples[i], 0.7); // bounds-checked\n }\n\n buf.WriteBlock(std2::move(samples));\n}\n```\n\nThe ` safe ` keyword on the function signature enables Circle's borrow checker for that function. You can mix safe and unsafe code at the function level, which is the most granular escape hatch of any language here.\n\nCircle compiles existing C++ codebases, even 180k-line projects, on the first try. It is a C++ compiler, so naturally it does. The migration path is: compile the code with Circle, then incrementally add ` #feature on safety ` to files one by one, fixing the safety violations the compiler reports.\n\n### The Bus Factor Problem\n\nCircle is maintained by one person. One incredibly talented person, but one person. There's no formal specification, no committee, no corporate backing. If Sean decides to pursue other interests, the project is effectively dead. For a hobby project, that's fine. For production infrastructure that needs to be maintained for a decade? That risk is real and it's the thing that limits broader recommendations of Circle.\n\n## Benchmark: Lock-Free Queue Implementation\n\nThe same Michael-Scott lock-free queue was implemented in all four languages plus baseline C++20. Here are the results on an [AWS](<https://aws.com/>) c7g.2xlarge (Graviton3, 8 vCPUs):\n\n| Language | Enqueue/Dequeue ops/sec | Binary Size | Compile Time |\n| --- | --- | --- | --- |\n| C++20 (baseline) | 14.2M | 48 KB | 1.8s |\n| Carbon | 13.8M | 52 KB | 4.2s |\n| Cpp2 (transpiled) | 14.1M | 48 KB | 2.1s |\n| Val | 11.6M | 67 KB | 12.4s |\n| Circle | 14.2M | 49 KB | 2.3s |\n\nCpp2 and Circle are essentially identical to baseline C++ in performance, which makes sense, they emit C++. Carbon is within 3%, which is impressive for a different compiler backend. Val's 18% penalty comes from its more conservative alias analysis; the compiler couldn't prove that certain pointer operations in the lock-free queue were non-aliasing.\n\n## Practical Recommendations\n\nFor organizations with an existing large C++ codebase and regulatory pressure to improve memory safety:\n\n1. **Circle** for the fastest path to safety annotations on existing code, for those who can accept the single-maintainer risk.\n2. **Cpp2** for the safest organizational bet, the output is standard C++, so you can always \"eject\" by just keeping the transpiled code.\n3. **Carbon** for those willing to wait 12-18 more months for the tooling to mature and the codebase is relatively clean modern C++.\n4. **Val** only for greenfield projects where you want the most elegant safety model and don't need C++ interop.\n\nNone of these are Rust. Rust has a decade head start in ecosystem, tooling, and production battle-testing. But for shops where 200 engineers know C++ and retraining to Rust would cost two years of velocity, these successors represent the pragmatic middle path.\n\nThe migration from C++ will not be a weekend project. It will not even be a quarter-long project for any non-trivial codebase. But the regulatory pressure is not going away, and starting now, even with imperfect tools, beats waiting for a perfect solution that may never arrive.\n\n## Frequently asked questions\n\n### Can Carbon actually interop with existing C++ codebases?\n\nYes, Carbon's interop layer can import C++ headers directly via \\`import Cpp\\`. However, heavily templated code, SFINAE-based APIs, and macro-heavy headers still cause issues. Clean, modern C++17/20 code interops best.\n\n### Is Cpp2/cppfront production-ready?\n\nNot yet for mission-critical systems. Cppfront transpiles to valid C++20, so the output is as production-ready as your C++ compiler, but the tooling, error messages, and ecosystem support are still maturing as of early 2026.\n\n### How does Val's ownership model differ from Rust's borrow checker?\n\nVal uses mutable value semantics rather than Rust's borrow-based approach. Instead of tracking references, Val ensures that every value has a single owner and mutations happen in-place. It avoids the 'fighting the borrow checker' experience by eliminating shared mutable references entirely.\n\n## Tips\n\n1. **Tip:** Avoid rewriting an entire codebase. Start by wrapping the most dangerous ` unsafe ` boundaries with a successor language module and interop back. Incremental migration is the only sane path.\n2. **Tip:** Carbon's ` import Cpp ` directive works best when your C++ headers are clean, no macros-as-API, no SFINAE tricks. Run ` clang-tidy ` modernize passes first.\n3. **Tip:** When evaluating Val, start with its ` mutableValue ` semantics on a small data-processing pipeline. The ownership model clicks faster when you see it applied to graph transformations.\n\n- c++\n- carbon\n- cpp2\n- val\n- circle\n- memory-safety\n- systems-programming\n- cppfront\n\n[Back to Blog](<https://lucioduran.com/blog>)","headings":[{"level":1,"text":"Memory-Safe C++ Alternatives: Carbon, Cpp2, Val, and Circle Compared","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#title"},{"level":2,"text":"The Problem That Persists","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#the-problem-that-persists"},{"level":2,"text":"Carbon: Google's Interop-First Bet","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#carbon-googles-interop-first-bet"},{"level":3,"text":"Interop in Practice","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#interop-in-practice"},{"level":3,"text":"Migration Cost Assessment","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#migration-cost-assessment"},{"level":2,"text":"Cpp2/cppfront: Herb Sutter's Pragmatic Middle Ground","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#cpp2cppfront-herb-sutters-pragmatic-middle-ground"},{"level":3,"text":"The Syntax in Action","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#the-syntax-in-action"},{"level":3,"text":"What Actually Transpiles","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#what-actually-transpiles"},{"level":3,"text":"The Catch","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#the-catch"},{"level":2,"text":"Val: The Mutable Value Semantics Approach","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#val-the-mutable-value-semantics-approach"},{"level":3,"text":"Ownership Without Borrowing","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#ownership-without-borrowing"},{"level":3,"text":"Current Limitations of Val","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#current-limitations-of-val"},{"level":2,"text":"Circle: Sean Baxter's One-Man Revolution","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#circle-sean-baxters-one-man-revolution"},{"level":3,"text":"Safety Annotations in Practice","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#safety-annotations-in-practice"},{"level":3,"text":"The Bus Factor Problem","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#the-bus-factor-problem"},{"level":2,"text":"Benchmark: Lock-Free Queue Implementation","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#benchmark-lock-free-queue-implementation"},{"level":2,"text":"Practical Recommendations","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#practical-recommendations"},{"level":2,"text":"Frequently asked questions","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#questions"},{"level":3,"text":"Can Carbon actually interop with existing C++ codebases?","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#question-1"},{"level":3,"text":"Is Cpp2/cppfront production-ready?","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#question-2"},{"level":3,"text":"How does Val's ownership model differ from Rust's borrow checker?","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#article-tips"}],"sections":[{"heading":"Memory-Safe C++ Alternatives: Carbon, Cpp2, Val, and Circle Compared","level":1,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val","content":"Programming Languages"},{"heading":"Memory-Safe C++ Alternatives: Carbon, Cpp2, Val, and Circle Compared","level":1,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#title","content":"2025-10-188 min read\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nAlso available in: [Español](<https://lucioduran.com/es/blog/memory-safe-c-alternatives-carbon-cpp2-val>)"},{"heading":"The Problem That Persists","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#the-problem-that-persists","content":"C++ doesn't have a memory safety story. It has a \"discipline and tooling\" story. AddressSanitizer catches bugs at runtime. Static analyzers catch some at compile time. The C++ Core Guidelines are a style guide, not an enforcement mechanism. And after three decades of incremental fixes, CVEs rooted in use-after-free still ship regularly and buffer overflows.\n\nThe NSA's 2022 guidance, CISA's subsequent push, and now the 2025 EU Cyber Resilience Act requirements have made \"just be more careful\" an untenable position for anything touching regulated industries. Hence the need to evaluate actual alternatives."},{"heading":"Carbon: Google's Interop-First Bet","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#carbon-googles-interop-first-bet","content":"Carbon's pitch is straightforward: be to C++ what Kotlin was to Java. Full bidirectional interop, gradual migration, and a modernized syntax that eliminates the most dangerous footguns."},{"heading":"Interop in Practice","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#interop-in-practice","content":"The following example imports a C++ audio buffer class in [Carbon](<https://github.com/carbon-language/carbon-lang>):\n\n```carbon\nimport Cpp library \"audio/ring_buffer.h\";\n\nfn ProcessBlock(buf: Cpp.RingBuffer*, block_size: i32) -> bool {\n var samples: auto = buf->ReadBlock(block_size);\n if (samples.size() != block_size) {\n return false;\n }\n\n // Carbon's bounds-checked access\n var i: i32 = 0;\n while (i < block_size) {\n samples[i] = ApplyGain(samples[i], 0.7);\n ++i;\n }\n\n return buf->WriteBlock(samples);\n}\n```\n\nThe ` import Cpp ` directive is genuinely impressive when it works. Testing shows that an entire ring buffer implementation, including ` std::atomic ` fence operations, can be imported and called from Carbon without writing a single line of binding code. The compiler understands C++ name mangling, vtable layouts, and even ` std::unique_ptr ` semantics.\n\nWhere it falls apart: SIMD code. Codebases using GCC vector extensions and ` __attribute__((target(\"avx2\"))) ` extensively will find that Carbon's interop layer chokes on these. Every SIMD function needs to be wrapped in a plain C-linkage wrapper, which defeats the purpose of seamless interop."},{"heading":"Migration Cost Assessment","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#migration-cost-assessment","content":"For a representative 180k-line codebase, the estimated breakdown:\n\n- **Clean modern C++17 code** (~60% of the codebase): ~2.5 engineer-months to port\n- **Template-heavy generic code** (~25%): ~6 engineer-months, with significant redesign\n- **SIMD/intrinsics/platform-specific** (~15%): Not feasible to port. Keep as C++ and interop.\n\nTotal realistic estimate: 8-10 engineer-months for an incomplete migration. That is the honest number rarely shared at conferences."},{"heading":"Cpp2/cppfront: Herb Sutter's Pragmatic Middle Ground","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#cpp2cppfront-herb-sutters-pragmatic-middle-ground","content":"Cpp2 takes a fundamentally different approach. Instead of building a new language with interop, it defines a new syntax that transpiles to standard C++20. Your existing build system, your existing debugger, your existing profiler, they all just work because the output is C++."},{"heading":"The Syntax in Action","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#the-syntax-in-action","content":"The same ring buffer processing logic in [Cpp2](<https://github.com/hsutter/cppfront>):\n\n```cpp2\nProcessBlock: (inout buf: RingBuffer, block_size: int) -> bool = {\n samples := buf.ReadBlock(block_size);\n if samples.size() != block_size {\n return false;\n }\n\n for (0 ..< block_size) do (i) {\n samples[i] = ApplyGain(samples[i], 0.7);\n }\n\n return buf.WriteBlock(samples);\n}\n```\n\nNotice the ` inout ` parameter, Cpp2 makes mutation explicit at the call site. Parameters are ` in ` (const ref) by default, which eliminates an entire class of accidental-mutation bugs."},{"heading":"What Actually Transpiles","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#what-actually-transpiles","content":"The cppfront compiler turns that into roughly this C++20:\n\n```cpp\nauto ProcessBlock(RingBuffer& buf, int block_size) -> bool {\n auto samples = buf.ReadBlock(block_size);\n if (samples.size() != block_size) {\n return false;\n }\n\n for (auto i = 0; i < block_size; ++i) {\n samples[i] = ApplyGain(samples[i], 0.7);\n }\n\n return buf.WriteBlock(samples);\n}\n```\n\nThe key insight: cppfront adds initialization safety (no uninitialized variables), null safety (no null dereference on ` cpp2::unique_ptr `), bounds safety (checked subscript by default), and type safety (no implicit narrowing conversions). All enforced at the Cpp2 syntax level, all compiled away to zero-overhead C++20."},{"heading":"The Catch","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#the-catch","content":"Cppfront's error messages are… not great. When something goes wrong during transpilation, you get an error pointing to a line in your Cpp2 source, but if the error is in the generated C++, your compiler barfs on generated code you didn't write. A common frustration: spending an entire afternoon debugging a template instantiation failure that turns out to be a missing ` requires ` clause that cppfront should have generated but did not.\n\nAlso, IDE support is limited. There's a VS Code extension with basic syntax highlighting, but no reliable go-to-definition, no refactoring tools, and the language server crashes on files over ~500 lines. This matters more than people think."},{"heading":"Val: The Mutable Value Semantics Approach","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#val-the-mutable-value-semantics-approach","content":"[Val](<https://www.val-lang.dev/>) (formerly known as Hylo) takes the most radical departure from C++. Its core premise: if every value is uniquely owned and mutations always happen in-place, you don't need a borrow checker, you don't need garbage collection, and you get memory safety with zero runtime overhead."},{"heading":"Ownership Without Borrowing","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#ownership-without-borrowing","content":"```val\nfun processBlock(buf: inout RingBuffer, blockSize: Int) -> Bool {\n var samples = buf.readBlock(blockSize)\n if samples.count != blockSize {\n return false\n }\n\n for i in 0 ..< blockSize {\n &samples[i] = applyGain(samples[i], 0.7)\n }\n\n return buf.writeBlock(samples)\n}\n```\n\nThe ` & ` prefix on ` samples[i] ` is Val's way of saying \"I'm mutating this in-place.\" If you forget it, the compiler refuses to compile. It's explicit, it's visible, and it eliminates the \"did I accidentally copy or alias this?\" class of bugs.\n\nWhat makes Val genuinely interesting compared to Rust is the absence of lifetime annotations. There are no ` 'a ` lifetimes sprinkled through your function signatures. Val's type system ensures safety through value semantics alone, if you have a value, you own it. Period. No shared mutable state, no data races by construction."},{"heading":"Current Limitations of Val","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#current-limitations-of-val","content":"Val is the youngest of the bunch, and it shows. The compiler (built on Swift's SIL) is slow, compiling a 3000-line test module took 47 seconds versus 3 seconds in Cpp2 and 8 seconds in Carbon. The standard library is minimal. There's no FFI story worth mentioning, you can call C functions through a ` @foreign ` annotation, but C++ interop doesn't exist yet.\n\nFor a greenfield project with full stack control? Val's design is arguably the most elegant. For migrating an existing C++ codebase? It's a non-starter in 2026."},{"heading":"Circle: Sean Baxter's One-Man Revolution","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#circle-sean-baxters-one-man-revolution","content":"[Circle](<https://www.circle-lang.org/>) is the wildcard. Sean Baxter has single-handedly built a C++ compiler that extends the language with first-class safety features, pattern matching, reflection, and what amounts to a new language that happens to be a strict superset of C++."},{"heading":"Safety Annotations in Practice","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#safety-annotations-in-practice","content":"```cpp\n#feature on safety\n\nvoid ProcessBlock(RingBuffer& buf, int block_size) safe {\n auto samples = buf.ReadBlock(block_size); // returns std2::vector\n if (samples.size() != block_size) {\n return;\n }\n\n for (int i = 0; i < block_size; ++i) {\n samples[i] = ApplyGain(samples[i], 0.7); // bounds-checked\n }\n\n buf.WriteBlock(std2::move(samples));\n}\n```\n\nThe ` safe ` keyword on the function signature enables Circle's borrow checker for that function. You can mix safe and unsafe code at the function level, which is the most granular escape hatch of any language here.\n\nCircle compiles existing C++ codebases, even 180k-line projects, on the first try. It is a C++ compiler, so naturally it does. The migration path is: compile the code with Circle, then incrementally add ` #feature on safety ` to files one by one, fixing the safety violations the compiler reports."},{"heading":"The Bus Factor Problem","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#the-bus-factor-problem","content":"Circle is maintained by one person. One incredibly talented person, but one person. There's no formal specification, no committee, no corporate backing. If Sean decides to pursue other interests, the project is effectively dead. For a hobby project, that's fine. For production infrastructure that needs to be maintained for a decade? That risk is real and it's the thing that limits broader recommendations of Circle."},{"heading":"Benchmark: Lock-Free Queue Implementation","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#benchmark-lock-free-queue-implementation","content":"The same Michael-Scott lock-free queue was implemented in all four languages plus baseline C++20. Here are the results on an [AWS](<https://aws.com/>) c7g.2xlarge (Graviton3, 8 vCPUs):\n\n| Language | Enqueue/Dequeue ops/sec | Binary Size | Compile Time |\n| --- | --- | --- | --- |\n| C++20 (baseline) | 14.2M | 48 KB | 1.8s |\n| Carbon | 13.8M | 52 KB | 4.2s |\n| Cpp2 (transpiled) | 14.1M | 48 KB | 2.1s |\n| Val | 11.6M | 67 KB | 12.4s |\n| Circle | 14.2M | 49 KB | 2.3s |\n\nCpp2 and Circle are essentially identical to baseline C++ in performance, which makes sense, they emit C++. Carbon is within 3%, which is impressive for a different compiler backend. Val's 18% penalty comes from its more conservative alias analysis; the compiler couldn't prove that certain pointer operations in the lock-free queue were non-aliasing."},{"heading":"Practical Recommendations","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#practical-recommendations","content":"For organizations with an existing large C++ codebase and regulatory pressure to improve memory safety:\n\n1. **Circle** for the fastest path to safety annotations on existing code, for those who can accept the single-maintainer risk.\n2. **Cpp2** for the safest organizational bet, the output is standard C++, so you can always \"eject\" by just keeping the transpiled code.\n3. **Carbon** for those willing to wait 12-18 more months for the tooling to mature and the codebase is relatively clean modern C++.\n4. **Val** only for greenfield projects where you want the most elegant safety model and don't need C++ interop.\n\nNone of these are Rust. Rust has a decade head start in ecosystem, tooling, and production battle-testing. But for shops where 200 engineers know C++ and retraining to Rust would cost two years of velocity, these successors represent the pragmatic middle path.\n\nThe migration from C++ will not be a weekend project. It will not even be a quarter-long project for any non-trivial codebase. But the regulatory pressure is not going away, and starting now, even with imperfect tools, beats waiting for a perfect solution that may never arrive."},{"heading":"Frequently asked questions","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#questions","content":""},{"heading":"Can Carbon actually interop with existing C++ codebases?","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#question-1","content":"Yes, Carbon's interop layer can import C++ headers directly via \\`import Cpp\\`. However, heavily templated code, SFINAE-based APIs, and macro-heavy headers still cause issues. Clean, modern C++17/20 code interops best."},{"heading":"Is Cpp2/cppfront production-ready?","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#question-2","content":"Not yet for mission-critical systems. Cppfront transpiles to valid C++20, so the output is as production-ready as your C++ compiler, but the tooling, error messages, and ecosystem support are still maturing as of early 2026."},{"heading":"How does Val's ownership model differ from Rust's borrow checker?","level":3,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#question-3","content":"Val uses mutable value semantics rather than Rust's borrow-based approach. Instead of tracking references, Val ensures that every value has a single owner and mutations happen in-place. It avoids the 'fighting the borrow checker' experience by eliminating shared mutable references entirely."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#article-tips","content":"1. **Tip:** Avoid rewriting an entire codebase. Start by wrapping the most dangerous ` unsafe ` boundaries with a successor language module and interop back. Incremental migration is the only sane path.\n2. **Tip:** Carbon's ` import Cpp ` directive works best when your C++ headers are clean, no macros-as-API, no SFINAE tricks. Run ` clang-tidy ` modernize passes first.\n3. **Tip:** When evaluating Val, start with its ` mutableValue ` semantics on a small data-processing pipeline. The ownership model clicks faster when you see it applied to graph transformations.\n\n- c++\n- carbon\n- cpp2\n- val\n- circle\n- memory-safety\n- systems-programming\n- cppfront\n\n[Back to Blog](<https://lucioduran.com/blog>)"}],"links":[{"text":"Español","url":"https://lucioduran.com/es/blog/memory-safe-c-alternatives-carbon-cpp2-val"},{"text":"Carbon","url":"https://github.com/carbon-language/carbon-lang"},{"text":"Cpp2","url":"https://github.com/hsutter/cppfront"},{"text":"Val","url":"https://www.val-lang.dev/"},{"text":"Circle","url":"https://www.circle-lang.org/"},{"text":"AWS","url":"https://aws.com/"},{"text":"Back to Blog","url":"https://lucioduran.com/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"BlogPosting","@id":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val#article","mainEntityOfPage":{"@type":"WebPage","@id":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val"},"headline":"Memory-Safe C++ Alternatives: Carbon, Cpp2, Val, and Circle Compared","description":"A technical comparison of the emerging C++ successor languages, Carbon, Cpp2/cppfront, Val, and Circle, with real interop examples, migration cost analysis, and objective assessments of current maturity.","datePublished":"2025-10-18","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"image":"https://lucioduran.com/favicons/og-default.png","url":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val","inLanguage":"en","isPartOf":{"@id":"https://lucioduran.com/blog#blog"},"articleSection":"Programming Languages","keywords":"c++, carbon, cpp2, val, circle, memory-safety, systems-programming, cppfront","wordCount":1491,"timeRequired":"PT8M","workTranslation":[{"@type":"BlogPosting","inLanguage":"es","url":"https://lucioduran.com/es/blog/memory-safe-c-alternatives-carbon-cpp2-val"}]},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://lucioduran.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/blog"},{"@type":"ListItem","position":3,"name":"Memory-Safe C++ Alternatives: Carbon, Cpp2, Val, and Circle Compared","item":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val"}]},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Can Carbon actually interop with existing C++ codebases?","acceptedAnswer":{"@type":"Answer","text":"Yes, Carbon's interop layer can import C++ headers directly via `import Cpp`. However, heavily templated code, SFINAE-based APIs, and macro-heavy headers still cause issues. Clean, modern C++17/20 code interops best."}},{"@type":"Question","name":"Is Cpp2/cppfront production-ready?","acceptedAnswer":{"@type":"Answer","text":"Not yet for mission-critical systems. Cppfront transpiles to valid C++20, so the output is as production-ready as your C++ compiler, but the tooling, error messages, and ecosystem support are still maturing as of early 2026."}},{"@type":"Question","name":"How does Val's ownership model differ from Rust's borrow checker?","acceptedAnswer":{"@type":"Answer","text":"Val uses mutable value semantics rather than Rust's borrow-based approach. Instead of tracking references, Val ensures that every value has a single owner and mutations happen in-place. It avoids the 'fighting the borrow checker' experience by eliminating shared mutable references entirely."}}]}]}],"contentHash":"dadb5c40d0a6ca4676032b17a40ef1dbc91c4987eb16af3f9dee4a66abc7ea2b","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/blog/memory-safe-c-alternatives-carbon-cpp2-val","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":65082,"contentBytes":12363}}
