Here is the new Carbon Copy, your periodic update on the Carbon language!
Carbon Copy is designed for those people who want a high-level view of what's happening on the project. If you'd like to subscribe, you can join [email protected]. Carbon Copy should arrive roughly (sometimes extremely roughly) every other month.
Toolchain update
Work on the toolchain continues. We have made strides in interop!
If you just want to say hello:
For more complicated interop, say we have the following circle.h:
The matching Carbon code:
You can try it yourself at the Compiler Explorer!
Spotlight: Classes Part II
In our last Carbon Copy Spotlight we talked a lot about classes. (If you haven't read it, start there!)
As we showed last time, you can initialize an object using a struct:
When Carbon initializes an object, it uses the fields in the struct literal to initialize fields in the class instance created on the left-hand side. This allows us to move away from specialized C++ constructor syntax and complex behavior, and move instead to using factory functions.
Factory functions are ordinary functions that can live as class functions on the class type or separately. Consider a solar system database, where we track various celestial objects. Here's how you could define a Planet class and a factory function:
Carbon aims for simplicity by avoiding special constructor rules, initializer lists, or separate rules for error handling during construction. Instead, it uses consistent function syntax and explicit initialization patterns that are familiar across the entire language.
Now, imagine we make Planet abstract, and make a non-abstract subclass called RingPlanet (you know, Saturn, Uranus, etc.).
As class hierarchies get deeper, each superclass will need its own .base and set of curly braces in the initialization, and that will get cumbersome. We could make factory functions for the base classes to simplify, but we have a problem:
What can we do?
Partial Classes: Building Blocks for Inheritance
Partial classes are a special construct used when working with inheritance. It represents the base class portion of an object that is not yet fully constructed.
Partial classes allow code within a derived class's construction process to initialize its base part before the fully-derived object is complete. You typically encounter partial types when implementing factory functions or C++-constructor-like logic for derived classes that extend other classes.
The partial type shares the same fields, order, and data layout as the complete base type, but it explicitly does not initialize its virtual pointer (vptr) slot. This distinction is critical because it prevents virtual method calls on an object that's still under construction (which otherwise could lead to undefined behavior). When the derived class is fully initialized and ready, the vptr slot is filled out.
Going back to our example, let's make a partial constructor. We'll make diameter private so that we couldn't use the technique above to set its value at RingPlanet creation time.
In this example a partial Planet represents the Planet portion of RingPlanet as it's being set up, ensuring that a derived class initialization can interact with the base.
If you think all the way back to Carbon Copy No.3, the partial Planet is part of an initializing expression. Initializing expressions require storage to be provided implicitly when evaluating the expression. In Make, the return value of Planet provides storage for the return value of Make. This avoids copying, which is both economical and also allows us to create base classes that can't be moved.
There is no reason to capture a partial Planet outside of an initializing expression, as it cannot (at this time) be used to construct a RingPlanet or other subclass. You cannot pass it to a function that takes a Planet, as partial Planet is not a Planet. This restriction, along with the unformed vptr, means that partially-formed instances cannot be accidentally used.
Destroying Objects: Destructors
Every type in Carbon is destructible, meaning it has a defined destructor method that runs when the lifetime of a value of that type ends, such as when a variable goes out of scope. You can customize a class's destructor by implementing the destroy method.
Expanding on our Planet example, if a Planet needed to perform some cleanup, like releasing a unique ID, you'd add a destroy method:
The destroy function has a standard definition, taking “ref self: Self” as its implicit parameter (which can be converted to a value form “self:Self”), and cannot have explicit parameters. This mechanism enhances practical safety and ensures cleanup actions can be performed automatically. Fields are destroyed in the reverse order they are created after the destructor returns. During interop, C++ has access to Carbon’s destructors, and Carbon has access to C++ destructors.
You might ask about virtual destructors. These are allowed, and required if you are trying to destroy a derived class through a pointer to its base class. It is erroneous behavior to destroy a derived class from a pointer to a base class that has a non-virtual destructor. With a virtual destructor, objects seen as abstract types also get cleaned up. If an abstract type needs a destructor, it should usually be virtual.
Conclusion
As we discussed in Part 1, classes are the primary mechanism for users to extend the Carbon type system. Part 2 talked about partial classes that make factory functions possible with abstract classes, and destructors that provide predictable ways to clean up your objects.
Check out these references for a deeper dive:
- Classes overview: https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md
- Inheritance proposal: https://github.com/carbon-language/carbon-lang/pull/777/files
- Destructors
- original proposal: Destructors #1154
- syntax proposal: Destructor syntax #5017
There are other aspects of Carbon's class system still under active development. These include advanced features which will be explored in future discussions as their designs mature. Stay tuned for more!
Carbon out and about
- Podcast: Carbon and Modernizing C++ with Chandler Carruth
- Jon Ross-Perkins: Carbon Language: How We Compile (slides)
- Scott Hanselman demos Carbon/C++ interop running on a Commodore 64 (!)
Recent proposals and issues
Recently accepted proposals including:
- #5914: Updating Carbon's safety strategy
- #5689: Semantic Identity and Order-Dependent Resolution for Rewrite Constraints
- #6008: Replace impl fn with override fn (new contributor!)
Recently closed leads issues including:
- #5930: How should modifiers for keyword operators be spelled?
- #5884: Should type-checking inside a facet type constraint have access to rewrite constraints?
If you want to keep up on Carbon’s proposals and issues, follow "Last Week In Carbon". Check out the RSS feed!
Wrap-up
Don't forget to subscribe! You can join [email protected]. If you have comments or would like to contribute to future editions of Carbon Copy, please reach out. And, always, join us any way you can!
Yours in hoping we can include more libraries,
Wolff, Josh, and the Carbon team