Claude Code Plugins

Community-maintained marketplace

Feedback

lang-carbon-library-dev

@aRustyDev/ai
0
0

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 Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name lang-carbon-library-dev
description 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.

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 lang-carbon-dev (if exists)
  • Deep C++ interop details - see lang-cpp-library-dev

Quick Reference

Task Pattern/Command
Create library Define in BUILD file with carbon_library()
Import C++ import Cpp library "path/to/header.h"
Export to C++ Use api access modifier
Generic type fn GenericFunc[T:! Type](x: T) -> T
Interface interface Comparable { ... }
Memory safety Use move semantics and ownership
Build bazel build //path/to:target
Test bazel test //path/to:target_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