Claude-skill-registry lang-carbon-dev
Foundational Carbon patterns covering memory safety, modern syntax, C++ interop, and Carbon idioms. Use when writing Carbon code or migrating from C++. This is the entry point for Carbon development.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/lang-carbon-dev" ~/.claude/skills/majiayu000-claude-skill-registry-lang-carbon-dev && rm -rf "$T"
skills/data/lang-carbon-dev/SKILL.mdCarbon Development Skill
Overview
Carbon is an experimental successor language to C++, designed by Google to address modern development needs while maintaining seamless bidirectional interoperability with existing C++ codebases. Carbon aims to provide memory safety, modern syntax, and better developer ergonomics while allowing incremental migration from C++.
Key Characteristics:
- Memory safety with compile-time guarantees (following Rust's direction)
- Seamless C++ interoperability without runtime overhead
- Modern generics system with both checked and template generics
- Pattern matching for control flow
- Explicit, lightweight error handling
- Designed for large-scale adoption and migration
Current Status (2025):
- Experimental language under active development
- 0.1 milestone targeted for end of 2026 (ambitious goal)
- Major focus on C++ interop demo and memory safety design
- Built with Bazel, compiler evaluation phase
Quick Reference
Basic Syntax
// Package declaration package Sample api; // Import statement import Main; // Function definition fn Add(a: i32, b: i32) -> i32 { return a + b; } // Main entry point fn Main() -> i32 { var result: i32 = Add(5, 10); Print("Result: {0}", result); return 0; }
Variable Declarations
// Mutable variable var x: i32 = 42; // Immutable variable (const) let y: i32 = 100; // Type inference var z: auto = 42; // inferred as i32 // Uninitialized (must initialize before use) var w: i32; w = 50;
Basic Types
// Integer types var a: i8 = 127; var b: i16 = 32767; var c: i32 = 2147483647; var d: i64 = 9223372036854775807; // Unsigned integers var ua: u8 = 255; var ub: u16 = 65535; var uc: u32 = 4294967295; var ud: u64 = 18446744073709551615; // Floating point var f: f32 = 3.14; var g: f64 = 2.718281828; // Boolean var flag: bool = true; // String var message: String = "Hello, Carbon!"; // Type alias alias MyInt = i32;
Functions
// Basic function fn Greet(name: String) -> String { return "Hello, " + name; } // Multiple parameters fn Calculate(x: i32, y: i32, operation: String) -> i32 { if (operation == "add") { return x + y; } return x - y; } // No return value (void) fn PrintMessage(msg: String) { Print(msg); } // Early return fn Divide(a: f64, b: f64) -> Optional(f64) { if (b == 0.0) { return Optional.None; } return Optional.Some(a / b); }
Control Flow
// If-else if (condition) { // do something } else if (other_condition) { // do something else } else { // default case } // While loop var i: i32 = 0; while (i < 10) { Print("{0}", i); i = i + 1; } // For loop for (var j: i32 = 0; j < 10; j = j + 1) { Print("{0}", j); } // Break and continue while (true) { if (should_exit) { break; } if (should_skip) { continue; } // normal iteration }
Pattern Matching
// Match statement (replaces switch) match (value) { case 0 => { Print("Zero"); } case 1 => { Print("One"); } case 2 | 3 | 4 => { Print("Two, three, or four"); } default => { Print("Something else"); } } // Match with patterns fn Classify(x: i32) -> String { return match (x) { case 0 => "zero", case 1 => "one", case n if n < 0 => "negative", case n if n > 0 and n < 10 => "small positive", default => "large positive", }; } // Destructuring in match match (result) { case Optional.Some(value) => { Print("Got value: {0}", value); } case Optional.None => { Print("No value"); } }
Core Concepts
Memory Safety
Carbon's memory safety strategy follows Rust's direction, using the type system for compile-time guarantees without runtime overhead.
Safety Categories:
-
Spatial Memory Safety: Protects against out-of-bounds access
- Array boundary checks
- Invalid pointer dereferencing
-
Temporal Memory Safety: Protects against use-after-free
- Heap use-after-free
- Stack use-after-return
Build Modes:
// Debug build: immediate runtime detection var array: [i32; 5] = [1, 2, 3, 4, 5]; var index: i32 = 10; // In debug mode: caught immediately var value: i32 = array[index]; // Performance build: undefined behavior if violated // Optimizer assumes no overflow/out-of-bounds var x: i32 = 2147483647; x = x + 1; // UB in performance mode // Hardened build: safe but potentially incorrect // Overflow won't crash but may produce wrong result // Or program will abort safely
Uninitialized State Tracking:
// Carbon tracks initialization better than C++ var x: i32; // Print(x); // Error: use of uninitialized variable x = 42; Print(x); // OK: initialized before use // Conditional initialization var y: i32; if (condition) { y = 10; } else { y = 20; } // OK: y is initialized in all paths Print(y);
Dynamic Bounds Checking:
// APIs designed for safety fn SafeAccess(array: [i32], index: i32) -> Optional(i32) { if (index < 0 or index >= array.size()) { return Optional.None; } return Optional.Some(array[index]); } // Usage match (SafeAccess(my_array, idx)) { case Optional.Some(value) => { Print("Value: {0}", value); } case Optional.None => { Print("Index out of bounds"); } }
C++ Interoperability
Carbon's primary design goal is seamless, bidirectional interoperability with C++.
Philosophy:
- Zero runtime overhead for interop calls
- No custom bridge code for simple types/functions
- Call Carbon from C++ and vice versa
- Works even with non-Carbon-aware C++ toolchains
- Incremental migration support
Calling C++ from Carbon:
// Import C++ header import Cpp library "mylib.h"; fn UseCppFunction() { // Call C++ function directly var result: i32 = Cpp.MyCppFunction(42); // Use C++ class var obj: Cpp.MyCppClass = Cpp.MyCppClass(); obj.Method(); } // C++ types are accessible fn ProcessVector(vec: Cpp.std.vector(i32)) { for (var i: i32 = 0; i < vec.size(); i = i + 1) { Print("{0}", vec[i]); } }
Calling Carbon from C++:
// C++ code #include "carbon_module.h" int main() { // Call Carbon function from C++ int result = Carbon::MyFunction(10, 20); // Use Carbon class Carbon::MyClass obj; obj.DoSomething(); return 0; }
Migration Strategy:
// Phase 1: Minimal migration // Auto-migrate C++ to interop-focused Carbon dialect // Maintains C++ semantics, minimal code changes // Phase 2: Incremental refactoring // Gradually adopt Carbon idioms // Introduce memory safety features // Modernize APIs and patterns // Example: C++ -> Carbon migration // C++ original: // void processData(std::vector<int>& data) { // for (auto& item : data) { // item *= 2; // } // } // Phase 1: Direct migration fn ProcessData(data: Cpp.std.vector(i32)*) { var i: i32 = 0; while (i < data->size()) { (*data)[i] = (*data)[i] * 2; i = i + 1; } } // Phase 2: Carbon idioms fn ProcessData(data: [i32]*) { for (var i: i32 = 0; i < data.size(); i = i + 1) { data[i] = data[i] * 2; } }
Safety with Interop:
// Carbon code has safety guarantees fn SafeCarbonFunction(x: i32) -> i32 { // Bounds checked, initialization verified var array: [i32; 5] = [1, 2, 3, 4, 5]; return array[x % 5]; // Safe modulo operation } // C++ interop accepts higher risk fn CallUnsafeCpp() { // C++ doesn't have same safety mechanisms // Carbon calling C++ accepts this risk unsafe { Cpp.LegacyFunction(); } } // Mitigation strategies fn SafeInterop(data: Cpp.std.vector(i32)) -> Optional(i32) { // Add runtime checks around C++ data if (data.empty()) { return Optional.None; } return Optional.Some(data[0]); }
Generics System
Carbon provides both checked generics and template generics for different use cases.
Checked Generics:
// Basic generic function fn GenericExample[T:! Type](x: T) -> T { return x; } // Usage with type inference fn Main() -> i32 { var int_val: i32 = GenericExample(42); var str_val: String = GenericExample("hello"); return 0; } // Generic with interface constraint interface Comparable { fn Compare[self: Self](other: Self) -> i32; } fn Max[T:! Comparable](a: T, b: T) -> T { if (a.Compare(b) > 0) { return a; } return b; }
Interfaces:
// Define an interface interface Vector { fn Add[self: Self](b: Self) -> Self; fn Scale[self: Self](v: f64) -> Self; } // Implement interface for a type class Vec2 { var x: f64; var y: f64; } impl Vec2 as Vector { fn Add[self: Self](b: Self) -> Self { return Vec2{.x = self.x + b.x, .y = self.y + b.y}; } fn Scale[self: Self](v: f64) -> Self { return Vec2{.x = self.x * v, .y = self.y * v}; } } // Generic function using interface fn Transform[T:! Vector](vec: T, factor: f64) -> T { return vec.Scale(factor); }
Template Generics:
// Template generic (C++ style) // Not checked at definition, checked at instantiation fn TemplateFunc[template T:! Type](x: T) -> T { // Can use any operations on T // Errors shown at instantiation site return x * 2 + 1; } // Useful for C++ interop fn ProcessCppContainer[template T:! Type](container: T) { // Works with any C++ container-like type for (var i: i32 = 0; i < container.size(); i = i + 1) { Print("{0}", container[i]); } }
Advantages of Checked Generics:
// Checked generics catch errors at definition fn BrokenGeneric[T:! Type](x: T) -> T { // Error: Type doesn't have '+' operator // return x + x; // Caught immediately } // Fix: add constraint interface Addable { fn Add[self: Self](other: Self) -> Self; } fn WorkingGeneric[T:! Addable](x: T) -> T { return x.Add(x); // OK: constraint guarantees Add exists } // Better error messages fn UseGeneric() { var s: String = "hello"; // Error: String doesn't implement Addable // var result: String = WorkingGeneric(s); // Clear message about missing interface }
Generic Types:
// Generic class class Container[T:! Type] { var data: [T]; fn Add[self: Self*](item: T) { self->data.push_back(item); } fn Get[self: Self](index: i32) -> Optional(T) { if (index < 0 or index >= self.data.size()) { return Optional.None; } return Optional.Some(self.data[index]); } } // Usage fn Main() -> i32 { var int_container: Container(i32) = Container(i32)(); int_container.Add(10); int_container.Add(20); match (int_container.Get(0)) { case Optional.Some(value) => { Print("First: {0}", value); } case Optional.None => { Print("Empty"); } } return 0; }
Type System
Primitive Types:
// Sized integers (guaranteed size) i8, i16, i32, i64 // Signed u8, u16, u32, u64 // Unsigned // Floating point f32, f64 // Boolean bool // String String // Pointer types T* // Mutable pointer const T* // Const pointer // Array types [T; N] // Fixed-size array [T] // Dynamic array (slice)
Type Aliases:
// Simple alias alias IntPtr = i32*; alias Callback = fn(i32) -> i32; // Generic alias alias Result[T:! Type, E:! Type] = variant { Ok(T), Err(E) }; // Usage fn Divide(a: i32, b: i32) -> Result(i32, String) { if (b == 0) { return Result.Err("Division by zero"); } return Result.Ok(a / b); }
Sum Types (Variants):
// Define a sum type variant Option[T:! Type] { Some(T), None } // Usage fn Find(array: [i32], target: i32) -> Option(i32) { for (var i: i32 = 0; i < array.size(); i = i + 1) { if (array[i] == target) { return Option.Some(i); } } return Option.None; } // Pattern matching on variants fn UseOption(opt: Option(i32)) { match (opt) { case Option.Some(value) => { Print("Found: {0}", value); } case Option.None => { Print("Not found"); } } }
Classes:
// Class definition class Point { var x: f64; var y: f64; // Constructor fn Create(x_val: f64, y_val: f64) -> Self { return Point{.x = x_val, .y = y_val}; } // Method fn Distance[self: Self](other: Self) -> f64 { var dx: f64 = self.x - other.x; var dy: f64 = self.y - other.y; return Math.Sqrt(dx * dx + dy * dy); } // Mutable method fn Move[self: Self*](dx: f64, dy: f64) { self->x = self->x + dx; self->y = self->y + dy; } } // Usage fn Main() -> i32 { var p1: Point = Point.Create(0.0, 0.0); var p2: Point = Point.Create(3.0, 4.0); var dist: f64 = p1.Distance(p2); Print("Distance: {0}", dist); p1.Move(1.0, 1.0); return 0; }
Inheritance and Composition:
// Base class class Shape { fn Area[self: Self] -> f64; } // Derived class class Circle { var radius: f64; base: Shape; } impl Circle as Shape { fn Area[self: Self] -> f64 { return 3.14159 * self.radius * self.radius; } } // Composition class Rectangle { var width: f64; var height: f64; } class ColoredRectangle { var rect: Rectangle; var color: String; fn Area[self: Self] -> f64 { return self.rect.width * self.rect.height; } }
Error Handling
Carbon uses explicit, statically-typed error handling without exceptions.
Basic Error Handling:
// Return sum type for errors variant Result[T:! Type, E:! Type] { Ok(T), Err(E) } // Function that can fail fn ParseInt(s: String) -> Result(i32, String) { if (s.IsEmpty()) { return Result.Err("Empty string"); } // ... parsing logic return Result.Ok(42); } // Handle errors fn UseParseInt(input: String) { match (ParseInt(input)) { case Result.Ok(value) => { Print("Parsed: {0}", value); } case Result.Err(error) => { Print("Error: {0}", error); } } }
Optional Type:
// Built-in optional type variant Optional[T:! Type] { Some(T), None } // Function returning optional fn GetConfig(key: String) -> Optional(String) { if (ConfigExists(key)) { return Optional.Some(GetConfigValue(key)); } return Optional.None; } // Unwrap with default fn GetOrDefault(opt: Optional(i32), default: i32) -> i32 { return match (opt) { case Optional.Some(value) => value, case Optional.None => default, }; }
Error Propagation:
// Error propagation operator (like Rust's ?) // Expected in future Carbon versions fn ProcessData() -> Result(i32, String) { var data: String = ReadFile("data.txt")?; var value: i32 = ParseInt(data)?; return Result.Ok(value * 2); } // Manual propagation (current approach) fn ProcessDataManual() -> Result(i32, String) { var file_result: Result(String, String) = ReadFile("data.txt"); match (file_result) { case Result.Err(e) => { return Result.Err(e); } case Result.Ok(data) => { var parse_result: Result(i32, String) = ParseInt(data); match (parse_result) { case Result.Err(e) => { return Result.Err(e); } case Result.Ok(value) => { return Result.Ok(value * 2); } } } } }
Error Context:
// Enriching errors with context variant Error { IoError(String), ParseError(String, i32), // message, line number ValidationError(String) } fn ReadConfig(path: String) -> Result(Config, Error) { var content_result: Result(String, String) = ReadFile(path); var content: String = match (content_result) { case Result.Err(e) => { return Result.Err(Error.IoError( "Failed to read " + path + ": " + e )); } case Result.Ok(c) => c, }; // Continue processing... return Result.Ok(config); }
No Exceptions:
// Carbon does NOT have exceptions // No try-catch blocks // No throw statements // Errors are explicit in function signatures // This forces explicit error handling fn RiskyOperation() -> Result(i32, String) { // Must return Result, can't throw if (something_wrong) { return Result.Err("Something went wrong"); } return Result.Ok(42); } // Caller must handle errors fn Caller() { var result: Result(i32, String) = RiskyOperation(); // Can't ignore the error - must handle it match (result) { case Result.Ok(value) => { /* use value */ } case Result.Err(e) => { /* handle error */ } } }
Build System
Bazel-Based Build:
# BUILD file for Carbon project load("@rules_carbon//carbon:defs.bzl", "carbon_library", "carbon_binary") carbon_library( name = "mylib", srcs = ["mylib.carbon"], deps = [ "//other:library", ], ) carbon_binary( name = "myapp", srcs = ["main.carbon"], deps = [ ":mylib", ], )
Compiler Usage:
# Compile Carbon source carbon compile main.carbon # Verbose output carbon -v compile main.carbon # Specify output carbon compile main.carbon -o output.o # Check syntax only carbon check main.carbon # Run different compilation phases carbon compile --phase=parse main.carbon carbon compile --phase=check main.carbon carbon compile --phase=lower main.carbon
Project Structure:
my_carbon_project/ ├── BUILD # Bazel build file ├── WORKSPACE # Bazel workspace ├── src/ │ ├── main.carbon # Main entry point │ ├── lib.carbon # Library code │ └── utils.carbon # Utilities ├── tests/ │ ├── BUILD │ └── test_lib.carbon # Tests └── api/ └── public.carbon # Public API
Package System:
// Package declaration package MyProject api; // Import from same package import .Utils; // Import from other package import OtherProject; // Import specific symbols import OtherProject.{Function, Type}; // Use imported code fn Main() -> i32 { Utils.Helper(); OtherProject.Function(); return 0; }
Common Patterns
Resource Management
// RAII-style resource management class File { var handle: FileHandle; fn Open(path: String) -> Result(Self, String) { var handle_result: Result(FileHandle, String) = OpenFileHandle(path); match (handle_result) { case Result.Err(e) => { return Result.Err(e); } case Result.Ok(h) => { return Result.Ok(File{.handle = h}); } } } fn Read[self: Self*](buffer: [u8]*) -> Result(i32, String) { return ReadFromHandle(self->handle, buffer); } // Destructor destructor [self: Self*] { CloseFileHandle(self->handle); } } // Usage - automatic cleanup fn ProcessFile(path: String) -> Result(i32, String) { var file_result: Result(File, String) = File.Open(path); match (file_result) { case Result.Err(e) => { return Result.Err(e); } case Result.Ok(file) => { var buffer: [u8; 1024] = [0; 1024]; var read_result: Result(i32, String) = file.Read(&buffer); // file destroyed automatically here return read_result; } } }
Builder Pattern
class ConfigBuilder { var host: Optional(String) = Optional.None; var port: Optional(i32) = Optional.None; var timeout: Optional(i32) = Optional.None; fn New() -> Self { return ConfigBuilder{}; } fn SetHost[self: Self*](host: String) -> Self* { self->host = Optional.Some(host); return self; } fn SetPort[self: Self*](port: i32) -> Self* { self->port = Optional.Some(port); return self; } fn SetTimeout[self: Self*](timeout: i32) -> Self* { self->timeout = Optional.Some(timeout); return self; } fn Build[self: Self] -> Result(Config, String) { var h: String = match (self.host) { case Optional.Some(v) => v, case Optional.None => { return Result.Err("Host is required"); } }; var p: i32 = GetOrDefault(self.port, 8080); var t: i32 = GetOrDefault(self.timeout, 30); return Result.Ok(Config{ .host = h, .port = p, .timeout = t }); } } // Usage fn CreateConfig() -> Result(Config, String) { var builder: ConfigBuilder = ConfigBuilder.New(); return builder .SetHost("localhost") .SetPort(3000) .SetTimeout(60) .Build(); }
Iterator Pattern
interface Iterator { fn Next[self: Self*] -> Optional(i32); } class RangeIterator { var current: i32; var end: i32; } impl RangeIterator as Iterator { fn Next[self: Self*] -> Optional(i32) { if (self->current >= self->end) { return Optional.None; } var value: i32 = self->current; self->current = self->current + 1; return Optional.Some(value); } } fn Range(start: i32, end: i32) -> RangeIterator { return RangeIterator{.current = start, .end = end}; } // Usage fn Main() -> i32 { var iter: RangeIterator = Range(0, 10); while (true) { match (iter.Next()) { case Optional.Some(value) => { Print("{0}", value); } case Optional.None => { break; } } } return 0; }
Visitor Pattern
// Define visitor interface interface NodeVisitor { fn VisitNumber[self: Self*](value: i32); fn VisitString[self: Self*](value: String); } // Define visitable nodes variant Node { Number(i32), String(String) } fn AcceptVisitor[V:! NodeVisitor](node: Node, visitor: V*) { match (node) { case Node.Number(value) => { visitor->VisitNumber(value); } case Node.String(value) => { visitor->VisitString(value); } } } // Implement concrete visitor class PrintVisitor {} impl PrintVisitor as NodeVisitor { fn VisitNumber[self: Self*](value: i32) { Print("Number: {0}", value); } fn VisitString[self: Self*](value: String) { Print("String: {0}", value); } } // Usage fn Main() -> i32 { var nodes: [Node] = [ Node.Number(42), Node.String("hello"), Node.Number(100) ]; var visitor: PrintVisitor = PrintVisitor{}; for (var i: i32 = 0; i < nodes.size(); i = i + 1) { AcceptVisitor(nodes[i], &visitor); } return 0; }
Factory Pattern
interface Shape { fn Area[self: Self] -> f64; fn Perimeter[self: Self] -> f64; } class Circle { var radius: f64; } impl Circle as Shape { fn Area[self: Self] -> f64 { return 3.14159 * self.radius * self.radius; } fn Perimeter[self: Self] -> f64 { return 2.0 * 3.14159 * self.radius; } } class Rectangle { var width: f64; var height: f64; } impl Rectangle as Shape { fn Area[self: Self] -> f64 { return self.width * self.height; } fn Perimeter[self: Self] -> f64 { return 2.0 * (self.width + self.height); } } // Factory function fn CreateShape(shape_type: String, params: [f64]) -> Optional(variant {Circle, Rectangle}) { if (shape_type == "circle" and params.size() >= 1) { return Optional.Some(Circle{.radius = params[0]}); } else if (shape_type == "rectangle" and params.size() >= 2) { return Optional.Some(Rectangle{ .width = params[0], .height = params[1] }); } return Optional.None; }
Migration from C++
Common C++ to Carbon Translations
Headers and Includes:
// C++ #include <vector> #include <string> #include "myheader.h"
// Carbon import Cpp library "vector"; import Cpp library "string"; import .MyModule;
Class Definition:
// C++ class MyClass { public: MyClass(int x, int y) : x_(x), y_(y) {} int getX() const { return x_; } void setX(int x) { x_ = x; } int compute() const { return x_ * y_; } private: int x_; int y_; };
// Carbon class MyClass { var x: i32; var y: i32; fn Create(x: i32, y: i32) -> Self { return MyClass{.x = x, .y = y}; } fn GetX[self: Self] -> i32 { return self.x; } fn SetX[self: Self*](x: i32) { self->x = x; } fn Compute[self: Self] -> i32 { return self.x * self.y; } }
Templates to Generics:
// C++ template<typename T> T max(T a, T b) { return (a > b) ? a : b; }
// Carbon - checked generic interface Comparable { fn Compare[self: Self](other: Self) -> i32; } fn Max[T:! Comparable](a: T, b: T) -> T { if (a.Compare(b) > 0) { return a; } return b; } // Or template generic for C++ compatibility fn MaxTemplate[template T:! Type](a: T, b: T) -> T { if (a > b) { return a; } return b; }
Smart Pointers:
// C++ std::unique_ptr<MyClass> obj = std::make_unique<MyClass>(10, 20); std::shared_ptr<MyClass> shared = std::make_shared<MyClass>(10, 20);
// Carbon - ownership via move semantics var obj: MyClass = MyClass.Create(10, 20); var moved_obj: MyClass = obj; // Move ownership // Reference counting (if needed) var shared: Shared(MyClass) = Shared.New(MyClass.Create(10, 20));
Exceptions to Results:
// C++ int divide(int a, int b) { if (b == 0) { throw std::runtime_error("Division by zero"); } return a / b; } try { int result = divide(10, 0); } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
// Carbon fn Divide(a: i32, b: i32) -> Result(i32, String) { if (b == 0) { return Result.Err("Division by zero"); } return Result.Ok(a / b); } match (Divide(10, 0)) { case Result.Ok(value) => { Print("Result: {0}", value); } case Result.Err(error) => { Print("Error: {0}", error); } }
Namespaces to Packages:
// C++ namespace MyNamespace { namespace Utils { void helper() { } } } MyNamespace::Utils::helper();
// Carbon package MyProject.Utils api; fn Helper() { } // Usage import MyProject.Utils; Utils.Helper();
Migration Checklist
Phase 1: Initial Setup
- Set up Carbon toolchain and build system (Bazel)
- Identify C++ libraries to keep vs migrate
- Create interop layer for C++ dependencies
- Set up testing infrastructure
Phase 2: Incremental Migration
- Start with leaf modules (no dependencies)
- Migrate simple utility functions first
- Convert headers to Carbon packages
- Update build files to include Carbon sources
- Ensure tests pass with mixed C++/Carbon codebase
Phase 3: Refactoring
- Replace raw pointers with Carbon ownership
- Convert exceptions to Result types
- Adopt Carbon idioms (match, generics)
- Add memory safety features
- Improve error handling
Phase 4: Optimization
- Profile mixed codebase
- Identify performance bottlenecks
- Optimize hot paths
- Reduce C++ interop overhead where possible
Carbon Idioms
Prefer Match Over If-Else Chains
// Less idiomatic fn Classify(x: i32) -> String { if (x == 0) { return "zero"; } else if (x > 0 and x < 10) { return "small"; } else if (x >= 10) { return "large"; } else { return "negative"; } } // More idiomatic fn Classify(x: i32) -> String { return match (x) { case 0 => "zero", case n if n > 0 and n < 10 => "small", case n if n >= 10 => "large", default => "negative", }; }
Use Type System for Validation
// Less idiomatic - runtime validation everywhere fn ProcessAge(age: i32) { if (age < 0 or age > 150) { Print("Invalid age"); return; } // use age } // More idiomatic - validated type class Age { var value: i32; fn Create(v: i32) -> Result(Self, String) { if (v < 0 or v > 150) { return Result.Err("Invalid age"); } return Result.Ok(Age{.value = v}); } } fn ProcessAge(age: Age) { // age is guaranteed valid Print("Age: {0}", age.value); }
Make Invalid States Unrepresentable
// Less idiomatic - multiple booleans class Connection { var is_connected: bool; var is_authenticated: bool; var is_encrypted: bool; // Can be in invalid states! } // More idiomatic - state machine with variants variant ConnectionState { Disconnected, Connected, Authenticated, Encrypted } class Connection { var state: ConnectionState; fn Connect[self: Self*] -> Result((), String) { match (self->state) { case ConnectionState.Disconnected => { self->state = ConnectionState.Connected; return Result.Ok(()); } default => { return Result.Err("Already connected"); } } } }
Prefer Immutability
// Less idiomatic fn ProcessData(data: [i32]*) { for (var i: i32 = 0; i < data->size(); i = i + 1) { data[i] = data[i] * 2; // Mutation } } // More idiomatic fn ProcessData(data: [i32]) -> [i32] { var result: [i32] = []; for (var i: i32 = 0; i < data.size(); i = i + 1) { result.push_back(data[i] * 2); } return result; }
Use Interfaces for Abstraction
// Less idiomatic - concrete types everywhere fn PrintCircle(c: Circle) { Print("Area: {0}", c.Area()); } fn PrintRectangle(r: Rectangle) { Print("Area: {0}", r.Area()); } // More idiomatic - interface abstraction interface Shape { fn Area[self: Self] -> f64; } fn PrintShape[T:! Shape](s: T) { Print("Area: {0}", s.Area()); }
Troubleshooting
Compilation Errors
Uninitialized Variable:
// Error var x: i32; Print(x); // Error: use of uninitialized variable // Fix var x: i32 = 0; Print(x);
Type Mismatch:
// Error var x: i32 = 42; var y: f64 = x; // Error: type mismatch // Fix var x: i32 = 42; var y: f64 = Convert.ToF64(x);
Missing Interface Implementation:
// Error fn UseComparable[T:! Comparable](x: T) { } class MyType { } fn Main() -> i32 { var obj: MyType = MyType{}; UseComparable(obj); // Error: MyType doesn't implement Comparable return 0; } // Fix impl MyType as Comparable { fn Compare[self: Self](other: Self) -> i32 { // Implementation return 0; } }
C++ Interop Issues
Header Not Found:
// Error import Cpp library "myheader.h"; // Error: file not found // Fix - check include paths in BUILD file carbon_library( name = "mylib", srcs = ["mylib.carbon"], copts = ["-I/path/to/headers"], )
Type Incompatibility:
// Error - C++ type not directly usable fn UseCppVector(vec: Cpp.std.vector(i32)) { vec[0] = 42; // Might fail } // Fix - use C++ API properly fn UseCppVector(vec: Cpp.std.vector(i32)*) { vec->at(0) = 42; // Use C++ methods }
Calling Convention Mismatch:
// Error - wrong calling convention for C++ function fn CallCppFunction() { Cpp.MyFunction(); // Might fail if signature wrong } // Fix - match C++ signature exactly import Cpp library "mylib.h"; fn CallCppFunction() { var result: i32 = Cpp.MyFunction(42, "hello"); }
Build System Issues
Missing Dependency:
# Error in BUILD file carbon_binary( name = "myapp", srcs = ["main.carbon"], # Missing dependency ) # Fix carbon_binary( name = "myapp", srcs = ["main.carbon"], deps = [ ":mylib", "//other:dependency", ], )
Circular Dependency:
# Error - circular deps carbon_library( name = "a", srcs = ["a.carbon"], deps = [":b"], ) carbon_library( name = "b", srcs = ["b.carbon"], deps = [":a"], # Circular! ) # Fix - refactor to break cycle carbon_library( name = "common", srcs = ["common.carbon"], ) carbon_library( name = "a", srcs = ["a.carbon"], deps = [":common"], ) carbon_library( name = "b", srcs = ["b.carbon"], deps = [":common"], )
Memory Safety Issues
Use After Move:
// Error var x: MyClass = MyClass.Create(); var y: MyClass = x; // x moved to y x.Method(); // Error: use after move // Fix - clone or use references var x: MyClass = MyClass.Create(); var y: MyClass* = &x; // Borrow, don't move x.Method(); // OK
Out of Bounds Access:
// Error in debug build var array: [i32; 5] = [1, 2, 3, 4, 5]; var value: i32 = array[10]; // Panic in debug mode // Fix - use safe access fn SafeGet(array: [i32], index: i32) -> Optional(i32) { if (index >= 0 and index < array.size()) { return Optional.Some(array[index]); } return Optional.None; }
Generic Type Issues
Constraint Not Satisfied:
// Error fn RequiresComparable[T:! Comparable](x: T) { } fn Main() -> i32 { var s: String = "hello"; RequiresComparable(s); // Error: String doesn't implement Comparable return 0; } // Fix - implement interface impl String as Comparable { fn Compare[self: Self](other: Self) -> i32 { // Implementation return 0; } }
Type Inference Failure:
// Error - can't infer type var x: auto = SomeGenericFunction(); // Ambiguous // Fix - specify type explicitly var x: i32 = SomeGenericFunction(); // Or provide type parameter var x: auto = SomeGenericFunction[i32]();
Best Practices
- Use checked generics by default, template generics only for C++ interop
- Make error handling explicit with Result/Optional types
- Prefer immutability unless mutation is clearly needed
- Use pattern matching instead of if-else chains
- Leverage the type system to prevent invalid states
- Follow RAII principles for resource management
- Start with C++ interop, gradually refactor to Carbon idioms
- Test extensively during migration from C++
- Use interfaces to define contracts and enable polymorphism
- Document safety assumptions especially around C++ interop
Zero and Default Values
Uninitialized Variables
// Variables must be initialized before use var x: i32; // Declared but uninitialized // Print(x); // Error: use of uninitialized variable x = 42; Print(x); // OK: now initialized // Conditional initialization - all paths must initialize var y: i32; if (condition) { y = 10; } else { y = 20; } Print(y); // OK: initialized in all paths
Default/Zero Values by Type
| Type | Zero Value | Initialization |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
| Array | Empty | |
| Optional | | |
| Pointer | null (unsafe) | Use Optional instead |
Struct/Class Initialization
class Config { var host: String; var port: i32; var debug: bool; } // All fields must be initialized var config: Config = Config{ .host = "localhost", .port = 8080, .debug = false }; // Factory function for defaults class Config { fn Default() -> Self { return Config{ .host = "localhost", .port = 8080, .debug = false }; } } var config: Config = Config.Default();
Optional for Nullable Values
// Carbon discourages null pointers // Use Optional instead fn FindUser(id: i32) -> Optional(User) { if (UserExists(id)) { return Optional.Some(GetUser(id)); } return Optional.None; } // Handle optionals fn GetUsername(id: i32) -> String { match (FindUser(id)) { case Optional.Some(user) => { return user.name; } case Optional.None => { return "Unknown"; } } } // Default value pattern fn GetOrDefault[T:! Type](opt: Optional(T), default: T) -> T { return match (opt) { case Optional.Some(value) => value, case Optional.None => default, }; }
Concurrency
Carbon's concurrency model is still under active design. The current focus is on memory safety and C++ interop before finalizing concurrency features.
Current Status (2025)
// Note: Concurrency syntax is subject to change // This represents expected patterns based on design documents // Thread safety through ownership // Carbon aims to prevent data races at compile time // Similar to Rust's Send/Sync traits class ThreadSafeCounter { var count: i32; fn Increment[self: Self*] { // Atomic or synchronized access (design pending) self->count = self->count + 1; } }
Expected Concurrency Patterns
// Async/await (expected in future versions) async fn FetchData(url: String) -> Result(Data, Error) { var response: Response = await Http.Get(url); return response.Body(); } // Structured concurrency (expected) fn ProcessConcurrently(items: [Item]) -> [Result] { return parallel for (item in items) { ProcessItem(item) }; }
C++ Interop for Concurrency
// For now, use C++ threading via interop import Cpp library "thread"; import Cpp library "mutex"; fn UseCppThreading() { var mutex: Cpp.std.mutex = Cpp.std.mutex(); // Lock guard pattern var lock: Cpp.std.lock_guard(Cpp.std.mutex) = Cpp.std.lock_guard(&mutex); // Critical section DoWork(); } // Dispatch to thread pool fn RunInBackground(task: fn()) { var thread: Cpp.std.thread = Cpp.std.thread(task); thread.detach(); }
Safety Philosophy
// Carbon's concurrency will emphasize: // 1. Compile-time data race prevention // 2. Ownership-based thread safety // 3. Explicit sharing markers // 4. Integration with memory safety model // Expected patterns: // - Values are "thread-local" by default // - Explicit markers for shared state // - Channels for communication (like Go/Rust) // - Actor model support (planned)
See
patterns-concurrency-dev for cross-language concurrency patterns.
Metaprogramming
Carbon provides compile-time metaprogramming through generics and template generics, but does NOT support runtime reflection.
Checked Generics
// Checked generics with interfaces interface Printable { fn Print[self: Self](); } fn PrintAll[T:! Printable](items: [T]) { for (var i: i32 = 0; i < items.size(); i = i + 1) { items[i].Print(); } } // Errors caught at definition site fn BrokenGeneric[T:! Type](x: T) -> T { // Error: Type doesn't guarantee '+' operator // return x + x; }
Template Generics
// Template generics (like C++ templates) // Unchecked at definition, checked at instantiation fn TemplateAdd[template T:! Type](a: T, b: T) -> T { return a + b; // Works if T has + operator } // Usage - errors at call site if T doesn't support + var result: i32 = TemplateAdd(5, 10); // OK // var bad: String = TemplateAdd("a", "b"); // Error at this line
Compile-Time Constants
// Compile-time values let PI: f64 = 3.14159265358979; let MAX_SIZE: i32 = 1024; // Generic with compile-time value class FixedArray[T:! Type, let N:! i32] { var data: [T; N]; fn Size[self: Self]() -> i32 { return N; // Compile-time constant } } var arr: FixedArray(i32, 10) = ...;
Interface-Based Polymorphism
// Define interface interface Serializable { fn ToBytes[self: Self]() -> [u8]; fn FromBytes(bytes: [u8]) -> Result(Self, String); } // Implement for type class User { var name: String; var age: i32; } impl User as Serializable { fn ToBytes[self: Self]() -> [u8] { var result: [u8] = []; // Serialization logic return result; } fn FromBytes(bytes: [u8]) -> Result(Self, String) { // Deserialization logic return Result.Ok(User{.name = "Alice", .age = 30}); } } // Generic function using interface fn Clone[T:! Serializable](obj: T) -> Result(T, String) { var bytes: [u8] = obj.ToBytes(); return T.FromBytes(bytes); }
No Runtime Reflection
// Carbon does NOT support runtime reflection like Java/C# // This is intentional for: // 1. Performance (no RTTI overhead) // 2. Safety (no dynamic type manipulation) // 3. Predictability (all types known at compile time) // Instead, use: // 1. Generics for type-safe polymorphism // 2. Interfaces for behavior abstraction // 3. Variants for tagged unions // 4. Pattern matching for type dispatch
See
patterns-metaprogramming-dev for cross-language metaprogramming patterns.
Serialization
Carbon does not yet have a standard serialization library. The expected patterns follow Carbon's type safety philosophy.
Manual Serialization Pattern
// Define serialization interface interface Serializable { fn ToJson[self: Self]() -> String; } interface Deserializable { fn FromJson(json: String) -> Result(Self, String); } // Implement for type class User { var name: String; var age: i32; var email: Optional(String); } impl User as Serializable { fn ToJson[self: Self]() -> String { var json: String = "{"; json = json + "\"name\":\"" + self.name + "\","; json = json + "\"age\":" + ToString(self.age); match (self.email) { case Optional.Some(e) => { json = json + ",\"email\":\"" + e + "\""; } case Optional.None => { // Omit null fields } } json = json + "}"; return json; } }
C++ Interop for JSON
// Use C++ JSON libraries via interop import Cpp library "nlohmann/json.hpp"; fn ParseJson(input: String) -> Result(Cpp.nlohmann.json, String) { // Use nlohmann::json via C++ interop var json: Cpp.nlohmann.json = Cpp.nlohmann.json.parse(input); return Result.Ok(json); } fn ToJsonString(data: Cpp.nlohmann.json) -> String { return data.dump(); } // Convert to Carbon types fn ParseUser(json: String) -> Result(User, String) { var parsed: Cpp.nlohmann.json = Cpp.nlohmann.json.parse(json); var user: User = User{ .name = parsed["name"].get[String](), .age = parsed["age"].get[i32](), .email = Optional.None }; if (parsed.contains("email")) { user.email = Optional.Some(parsed["email"].get[String]()); } return Result.Ok(user); }
Validation Pattern
// Validate after deserialization class User { var name: String; var age: i32; fn Validate[self: Self]() -> Result((), String) { if (self.name.IsEmpty()) { return Result.Err("Name cannot be empty"); } if (self.age < 0 or self.age > 150) { return Result.Err("Invalid age"); } return Result.Ok(()); } } fn ParseAndValidateUser(json: String) -> Result(User, String) { var user: User = ParseUser(json)?; user.Validate()?; return Result.Ok(user); }
See
patterns-serialization-dev for cross-language serialization patterns.
Testing
Carbon uses Bazel for its build and test system. Testing patterns follow structured conventions.
Basic Test Structure
// test_mylib.carbon package MyLib testing; import MyLib; import Testing; fn TestAdd() { Testing.AssertEqual(MyLib.Add(2, 3), 5); } fn TestAddNegative() { Testing.AssertEqual(MyLib.Add(-1, 1), 0); } fn TestAddZero() { Testing.AssertEqual(MyLib.Add(0, 0), 0); }
Bazel Test Configuration
# BUILD file load("@rules_carbon//carbon:defs.bzl", "carbon_test") carbon_test( name = "mylib_test", srcs = ["test_mylib.carbon"], deps = [ ":mylib", "@carbon_testing//:testing", ], )
Assertion Patterns
import Testing; fn TestAssertions() { // Equality Testing.AssertEqual(actual, expected); Testing.AssertNotEqual(value1, value2); // Boolean Testing.AssertTrue(condition); Testing.AssertFalse(condition); // Optional Testing.AssertSome(optional); Testing.AssertNone(optional); // Result Testing.AssertOk(result); Testing.AssertErr(result); // Comparison Testing.AssertGreaterThan(5, 3); Testing.AssertLessThan(3, 5); }
Testing with Optional and Result
fn TestOptionalHandling() { var result: Optional(i32) = FindValue(key); match (result) { case Optional.Some(value) => { Testing.AssertEqual(value, expectedValue); } case Optional.None => { Testing.Fail("Expected value but got None"); } } } fn TestResultHandling() { var result: Result(User, String) = ParseUser(validJson); match (result) { case Result.Ok(user) => { Testing.AssertEqual(user.name, "Alice"); } case Result.Err(error) => { Testing.Fail("Unexpected error: " + error); } } }
Test Organization
my_project/ ├── src/ │ ├── BUILD │ ├── lib.carbon │ └── utils.carbon └── tests/ ├── BUILD ├── test_lib.carbon └── test_utils.carbon
Mocking via Interfaces
// Define interface for dependency interface Database { fn Get[self: Self](key: String) -> Optional(String); fn Set[self: Self*](key: String, value: String); } // Production implementation class RealDatabase { // Real database connection } impl RealDatabase as Database { fn Get[self: Self](key: String) -> Optional(String) { // Real implementation } fn Set[self: Self*](key: String, value: String) { // Real implementation } } // Test mock class MockDatabase { var data: Map(String, String); } impl MockDatabase as Database { fn Get[self: Self](key: String) -> Optional(String) { return self.data.Get(key); } fn Set[self: Self*](key: String, value: String) { self.data.Set(key, value); } } // Test using mock fn TestServiceWithMock() { var mock: MockDatabase = MockDatabase{.data = Map()}; mock.Set("key", "value"); var service: Service(MockDatabase) = Service(&mock); var result: String = service.Process("key"); Testing.AssertEqual(result, "processed: value"); }
Cross-Cutting Patterns
For cross-language comparison and translation patterns, see:
- Async patterns (when Carbon supports them)patterns-concurrency-dev
- JSON, validation patternspatterns-serialization-dev
- Generics, interface patternspatterns-metaprogramming-dev
Resources
Official Documentation
Design Documents
Community
Note: Carbon is experimental and under active development. The 0.1 milestone is targeted for late 2026. Syntax and features shown in this skill are based on current design proposals and may change. Always refer to the official documentation for the most up-to-date information.