Agents lang-carbon-library-dev
Carbon language library development patterns including interoperability with C++, memory safety patterns, generic programming, build system integration, testing approaches, and documentation standards. Use when creating Carbon libraries, designing public APIs with safety and interoperability in mind, or porting C++ libraries to Carbon.
install
source · Clone the upstream repo
git clone https://github.com/aRustyDev/agents
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aRustyDev/agents "$T" && mkdir -p ~/.claude/skills && cp -r "$T/content/skills/lang-carbon-library-dev" ~/.claude/skills/arustydev-agents-lang-carbon-library-dev && rm -rf "$T"
manifest:
content/skills/lang-carbon-library-dev/SKILL.mdsource content
Carbon Library Development
Patterns for developing libraries in the Carbon language, with emphasis on C++ interoperability, memory safety, and modern design patterns.
Overview
This skill covers:
- Carbon language fundamentals for library authors
- Interoperability with C++ codebases
- Memory safety patterns and ownership
- Generic programming with Carbon's type system
- Build system integration (Bazel)
- Testing approaches and documentation standards
- API design for both Carbon and C++ consumers
This skill does NOT cover:
- General library patterns - see
meta-library-dev - Application development - see
(if exists)lang-carbon-dev - Deep C++ interop details - see
lang-cpp-library-dev
Quick Reference
| Task | Pattern/Command |
|---|---|
| Create library | Define in BUILD file with |
| Import C++ | |
| Export to C++ | Use access modifier |
| Generic type | |
| Interface | |
| Memory safety | Use move semantics and ownership |
| Build | |
| Test | |
Carbon Language Fundamentals
Package and API Structure
// my_library/types.carbon package MyLibrary; // Public API (visible to other packages) api class PublicType { var value: i32; // Public constructor fn Create(v: i32) -> Self { return {.value = v}; } // Public method fn GetValue[self: Self]() -> i32 { return self.value; } } // Internal type (package-private) class InternalHelper { var data: String; } // Public function api fn ProcessData(input: String) -> PublicType { var helper: InternalHelper = {.data = input}; return PublicType.Create(helper.data.Length()); }
Access Control
// api - Visible outside the package (public API) api class PublicClass { ... } // private - Visible only in current file (default) private class FilePrivateClass { ... } // Default (no modifier) is private class DefaultPrivateClass { ... }
Memory Safety and Ownership
Move Semantics
// Move-only type class UniqueResource { var handle: i64; // Destructor called when value goes out of scope destructor { ReleaseHandle(handle); } // Explicitly deleted copy constructor fn Copy[self: Self]() -> Self = delete; // Move constructor (transfer ownership) fn Move[self: Self*]() -> Self { var result: Self = {.handle = self->handle}; self->handle = -1; // Invalidate source return result; } } // Usage fn UseResource() { var resource: UniqueResource = AcquireResource(); // resource is automatically destroyed here }
Reference Types and Borrowing
// Immutable reference (borrow) fn ReadData[data: String*]() -> i32 { return data->Length(); } // Mutable reference fn ModifyData[data: String*]() { data->Append(" modified"); } // Pointer parameters for optional references fn OptionalData[data: Optional(String*)]() { if (data) { Print(data.Value()->Length()); } }
Safety Patterns
// Use Option type for nullable values api fn FindElement[T:! Type](vec: Vector(T), predicate: fn(T) -> Bool) -> Option(T) { for (item: T in vec) { if (predicate(item)) { return Option(T).Some(item); } } return Option(T).None(); } // Result type for error handling api fn ParseInteger(input: String) -> Result(i32, ParseError) { if (input.IsEmpty()) { return Result(i32, ParseError).Err( ParseError.EmptyInput() ); } var value: i32 = ConvertToInt(input); return Result(i32, ParseError).Ok(value); }
Generic Programming
Generic Functions
// Simple generic function api fn Swap[T:! Type](a: T*, b: T*) { var temp: T = a->Move(); a = b->Move(); b = temp.Move(); } // Constrained generics with interfaces interface Comparable { fn Compare[self: Self](other: Self) -> i32; } api fn Max[T:! Comparable](a: T, b: T) -> T { if (a.Compare(b) > 0) { return a; } return b; }
Generic Types
// Generic container api class Container[T:! Type] { var data: Vector(T); fn Create() -> Self { return {.data = Vector(T).Create()}; } fn Add[self: Self*](item: T) { self->data.Push(item); } fn Get[self: Self*](index: i32) -> T* { return self->data.At(index); } fn Size[self: Self]() -> i32 { return self.data.Size(); } } // Usage var intContainer: Container(i32) = Container(i32).Create(); intContainer.Add(42);
Interface-Based Generics
// Define interface interface Serializable { fn Serialize[self: Self]() -> String; fn Deserialize(data: String) -> Self; } // Implement for custom type class MyData { var value: i32; var name: String; } impl MyData as Serializable { fn Serialize[self: Self]() -> String { return String.Format("{value: {0}, name: {1}}", self.value, self.name); } fn Deserialize(data: String) -> Self { // Parse and construct return {.value = 0, .name = data}; } } // Generic function using interface api fn SaveToFile[T:! Serializable](object: T, path: String) { var data: String = object.Serialize(); WriteFile(path, data); }
C++ Interoperability
Importing C++ Libraries
// Import C++ header import Cpp library "vector"; import Cpp library "string"; import Cpp library "memory"; // Use C++ types fn UseCppVector() { var vec: Cpp.std.vector(i32) = Cpp.std.vector(i32).new(); vec.push_back(1); vec.push_back(2); vec.push_back(3); }
Wrapping C++ APIs
// Wrap C++ API with Carbon-friendly interface import Cpp library "legacy_api.h"; api class LegacyWrapper { private var cppHandle: Cpp.LegacyHandle*; fn Create(config: String) -> Self { var handle: Cpp.LegacyHandle* = Cpp.CreateLegacyHandle(config.ToCppString()); return {.cppHandle = handle}; } destructor { Cpp.DestroyLegacyHandle(cppHandle); } // Safe Carbon API fn ProcessData[self: Self*](input: String) -> Result(String, Error) { var cppResult: Cpp.std.string = Cpp.ProcessWithHandle(self->cppHandle, input.ToCppString()); if (Cpp.HasError(self->cppHandle)) { return Result(String, Error).Err(Error.ProcessingFailed()); } return Result(String, Error).Ok( String.FromCppString(cppResult) ); } }
Exporting to C++
// Carbon API that can be called from C++ api class CarbonLibrary { // Use simple types for C++ compatibility api fn ComputeHash(input: String) -> i64 { var hash: i64 = 0; for (ch: i8 in input) { hash = hash * 31 + ch; } return hash; } // Complex types require wrapping api fn ProcessVector(vec: Vector(i32)) -> i32 { var sum: i32 = 0; for (val: i32 in vec) { sum += val; } return sum; } }
Type Compatibility
// Carbon to C++ type mapping // i8, i16, i32, i64 -> int8_t, int16_t, int32_t, int64_t // u8, u16, u32, u64 -> uint8_t, uint16_t, uint32_t, uint64_t // f32, f64 -> float, double // bool -> bool // String -> std::string (with conversion) // Conversion helpers fn ToCppString(s: String) -> Cpp.std.string { return Cpp.std.string.new(s.Data(), s.Length()); } fn FromCppString(cpp: Cpp.std.string) -> String { return String.FromBytes(cpp.data(), cpp.size()); }
Build System Integration
Bazel BUILD File
# BUILD file for Carbon library load("@rules_carbon//carbon:defs.bzl", "carbon_library", "carbon_test") carbon_library( name = "my_library", srcs = [ "types.carbon", "functions.carbon", ], hdrs = [ "api.carbon", ], deps = [ ":internal_lib", "@carbon_lang//common:string", ], visibility = ["//visibility:public"], ) carbon_library( name = "internal_lib", srcs = ["internal.carbon"], visibility = ["//visibility:private"], ) # With C++ interop carbon_library( name = "cpp_wrapper", srcs = ["wrapper.carbon"], deps = [ "@legacy_cpp_lib//:library", ], cpp_deps = True, )
Project Structure
my_carbon_library/ ├── BUILD # Bazel build configuration ├── WORKSPACE # Bazel workspace ├── README.md ├── LICENSE ├── api/ # Public API │ ├── BUILD │ └── public_types.carbon ├── src/ # Implementation │ ├── BUILD │ ├── impl.carbon │ └── internal/ │ ├── BUILD │ └── helpers.carbon ├── tests/ # Tests │ ├── BUILD │ └── api_test.carbon └── examples/ # Example usage ├── BUILD └── basic_usage.carbon
Testing
Unit Tests
// tests/my_library_test.carbon package MyLibraryTest; import MyLibrary; import Testing; @Test fn TestPublicType() { var obj: MyLibrary.PublicType = MyLibrary.PublicType.Create(42); Testing.ExpectEq(obj.GetValue(), 42); } @Test fn TestProcessData() { var result: MyLibrary.PublicType = MyLibrary.ProcessData("hello"); Testing.ExpectEq(result.GetValue(), 5); } @Test fn TestErrorHandling() { var result: Result(i32, ParseError) = MyLibrary.ParseInteger(""); Testing.ExpectTrue(result.IsErr()); }
Test BUILD Configuration
carbon_test( name = "my_library_test", srcs = ["my_library_test.carbon"], deps = [ "//my_library:my_library", ], size = "small", )
Property-Based Testing
// Advanced testing patterns @Test fn TestRoundtrip() { var inputs: Vector(String) = GenerateTestStrings(); for (input: String in inputs) { var encoded: String = Encode(input); var decoded: Result(String, Error) = Decode(encoded); Testing.ExpectTrue(decoded.IsOk()); Testing.ExpectEq(decoded.Unwrap(), input); } }
Documentation
Doc Comments
/// Represents a configuration for the library. /// /// # Example /// ``` /// var config: Config = Config.Default(); /// config.SetTimeout(30); /// ``` api class Config { private var timeout: i32; /// Creates a configuration with default values. /// /// Returns: A Config with timeout set to 10 seconds. fn Default() -> Self { return {.timeout = 10}; } /// Sets the timeout value. /// /// Parameters: /// - seconds: The timeout in seconds (must be positive) /// /// Note: Values greater than 300 will be capped at 300. fn SetTimeout[self: Self*](seconds: i32) { if (seconds > 300) { self->timeout = 300; } else if (seconds > 0) { self->timeout = seconds; } } }
Package Documentation
/// # MyLibrary /// /// A Carbon library for efficient data processing with C++ interoperability. /// /// ## Features /// /// - Type-safe generic containers /// - Zero-cost C++ interop /// - Memory-safe by default /// - High-performance processing /// /// ## Quick Start /// /// ```carbon /// import MyLibrary; /// /// fn main() -> i32 { /// var processor: MyLibrary.Processor = MyLibrary.Processor.Create(); /// var result: String = processor.Process("input data"); /// Print(result); /// return 0; /// } /// ``` package MyLibrary;
API Design Best Practices
1. Use Strong Types
// Good: Type-safe ID api class UserId { private var id: i64; fn Create(id: i64) -> Self { return {.id = id}; } fn ToInt[self: Self]() -> i64 { return self.id; } } // Avoid: Primitive obsession api fn GetUser(userId: i64) -> User; // Can confuse with other IDs
2. Prefer Immutability
// Good: Immutable by default api class ImmutableConfig { let timeout: i32; let retries: i32; fn WithTimeout[self: Self](newTimeout: i32) -> Self { return {.timeout = newTimeout, .retries = self.retries}; } } // Mutable when needed api class MutableBuffer { var data: Vector(u8); fn Append[self: Self*](bytes: Vector(u8)) { self->data.Extend(bytes); } }
3. Clear Error Handling
// Good: Explicit error types api class FileError { choice { NotFound, PermissionDenied, IoError(String), } } api fn ReadFile(path: String) -> Result(String, FileError) { // Implementation } // Usage encourages error handling var content: Result(String, FileError) = ReadFile("data.txt"); match (content) { case Ok(text: String) => { Process(text); } case Err(FileError.NotFound) => { Print("File not found"); } case Err(error: FileError) => { Print("Error: {0}", error); } }
4. Leverage Generics
// Good: Generic and reusable api fn Map[T:! Type, U:! Type]( vec: Vector(T), transform: fn(T) -> U ) -> Vector(U) { var result: Vector(U) = Vector(U).Create(); for (item: T in vec) { result.Push(transform(item)); } return result; }
Performance Considerations
Move Semantics
// Efficient: Move instead of copy api fn ProcessLargeData(data: LargeBuffer) -> LargeBuffer { var result: LargeBuffer = Transform(data.Move()); return result.Move(); // Return by move }
Inline Functions
// Hot path functions should be small and inline @Inline fn FastHash[self: String]() -> i64 { var hash: i64 = 0; for (ch: i8 in self) { hash = (hash << 5) - hash + ch; } return hash; }
Zero-Cost Abstractions
// Iterator abstraction with no runtime cost api class RangeIterator { var current: i32; var end: i32; @Inline fn Next[self: Self*]() -> Option(i32) { if (self->current < self->end) { var value: i32 = self->current; self->current += 1; return Option(i32).Some(value); } return Option(i32).None(); } }
Migration from C++
Gradual Migration Strategy
// 1. Start by wrapping C++ APIs import Cpp library "legacy.h"; api class CarbonFacade { private var cppImpl: Cpp.LegacyClass*; fn Create() -> Self { return {.cppImpl = Cpp.LegacyClass.new()}; } fn Operation[self: Self*](input: String) -> String { return FromCppString( self->cppImpl->operation(ToCppString(input)) ); } } // 2. Gradually rewrite implementation in Carbon api class CarbonNativeImpl { // Pure Carbon implementation fn Operation[self: Self*](input: String) -> String { // Carbon implementation } }
Compatibility Patterns
// Maintain C++ compatibility during migration api class HybridLibrary { choice { CppBackend(Cpp.LegacyImpl*), CarbonBackend(CarbonImpl), } fn Operation[self: Self*](input: String) -> String { match (self) { case CppBackend(impl: Cpp.LegacyImpl*) => { return FromCppString(impl->operation(ToCppString(input))); } case CarbonBackend(impl: CarbonImpl) => { return impl.Operation(input); } } } }
Common Patterns
Builder Pattern
api class ConfigBuilder { var timeout: Option(i32); var retries: Option(i32); var strictMode: bool; fn Create() -> Self { return { .timeout = Option(i32).None(), .retries = Option(i32).None(), .strictMode = false }; } fn WithTimeout[self: Self](t: i32) -> Self { return { .timeout = Option(i32).Some(t), .retries = self.retries, .strictMode = self.strictMode }; } fn Build[self: Self]() -> Config { return { .timeout = self.timeout.UnwrapOr(30), .retries = self.retries.UnwrapOr(3), .strictMode = self.strictMode }; } }
Factory Pattern
api interface Parser { fn Parse[self: Self*](input: String) -> Result(Document, ParseError); } api class ParserFactory { fn CreateParser(format: String) -> Option(Parser) { if (format == "json") { return Option(Parser).Some(JsonParser.Create()); } else if (format == "xml") { return Option(Parser).Some(XmlParser.Create()); } return Option(Parser).None(); } }
Troubleshooting
Ownership Errors
// Problem: Using moved value var data: String = "hello"; var moved: String = data.Move(); Print(data); // ERROR: data was moved // Fix: Don't use after move, or use reference var data: String = "hello"; var moved: String = data.Move(); Print(moved); // OK
C++ Interop Issues
// Problem: Memory management mismatch import Cpp library "api.h"; fn LeakyFunction() { var cppPtr: Cpp.Object* = Cpp.CreateObject(); // Forgot to delete - memory leak } // Fix: Use RAII wrapper class CppObjectWrapper { var ptr: Cpp.Object*; destructor { Cpp.DeleteObject(ptr); } }
Generic Constraints
// Problem: Missing constraint fn Compare[T:! Type](a: T, b: T) -> bool { return a < b; // ERROR: T doesn't have < operator } // Fix: Add proper constraint interface Ordered { fn Less[self: Self](other: Self) -> bool; } fn Compare[T:! Ordered](a: T, b: T) -> bool { return a.Less(b); }
References
- Carbon Language Documentation
- Carbon Language Spec
- C++ Interoperability Guide
- General library patternsmeta-library-dev
- C++ library developmentlang-cpp-library-dev