After removing the interface, inversion of control, and protected access capabilities from traditional inheritance, what do we have left (besides composition)? This is what we have: placing a few extra tokens on a derived class causes all named fields and methods of one or more base classes to be absorbed as if explicitly incorporated. Further, certain inherited methods can be customized (overridden) with their own implementation. The primary selling point for inheritance has always been this sort of code reuse.
Anti-inheritance advocates are likely to enthusiastically support this post. It promotes the most useful feature of traditional inheritance (Interfaces), turning it into a more valuable abstraction that is largely independent of inheritance. It discards two other traditional features of inheritance, Inversion of Control and Protected Access, as both unnecessary and dangerous. Let’s examine each in turn… Interfaces An interface is “an abstract type that contains no data but defines behaviors as method signatures.
I need to decide what sort of inheritance capability Cone will offer. None! I can hear some of you insist. “Inheritance has recently fallen out of favor as a programming design solution,” claims the Rust language book. “Favor object composition over class inheritance,” recommends the Design Patterns book in 1994. “Inheritance is Evil.” insists Nicolò Pignatelli. It is not hard to find cogent, hard-hitting critiques of inheritance, complaining about costs incurred from fragile base classes, excessive coupling, and broken encapsulation.
The execution of a program unfolds over some interval of time. The lifetime of every temporary resource (e.g., variable or object) is the time span between that resource’s “creation” and “destruction”. This lifetime is wholly contained within the typically-longer lifetime of the program. The goal of this post is to explore how versatile lifetime analysis has increasingly become in managing memory efficiently, safely and with better performance. By the end of this post, we will explore exciting new ways to apply lifetime analysis, beyond their current support in Rust.
Note: You can read Russian translation here. I have always wanted the Cone compiler to be fast. Faster build times make it easier and more pleasant for the programmer to iterate and experiment quickly. Compilers built for speed, such as Turbo Pascal, D and Go, win praise and loyalty. Languages that struggle more with this, like C++ or Rust, get regular complaints and requests to speed up build times.
The decades-long golden age of Moore’s Law is fading. Slow, bloated software will find it increasingly difficult to hide behind the skirt of ever-faster computer hardware. Given that businesses and users will not yield on their demand for speedy software, developers will need to get a lot better at architecting for performance. In his excellent article on Performance Culture, Joe Duffy begins: “Performance is one of the key pillars of software engineering, and is something that’s hard to do right, and sometimes even difficult to recognize.
To complete our three-part series on permissions, which began with Race-Safe Strategies, let’s talk about the transitional nature of reference permissions. When are permissions transitional? When we can safely create a copy of a reference which has a different permission than the reference it copied from. There are several ways in which this can happen, which this diagram summarizes (and the following sections explain): The following sections describe the nature of several one-way transitions that flow downward in the diagram.
In my last post, Race-safe Strategies, one footnote stated “safety issues which look suspiciously similar to race conditions can crop up when a language supports the creation of “interior references” to shared, mutable values of certain types”. Let’s explore that now. I will begin by recapitulating Manish Goregaokar’s excellent post “The Problem With Single-Threaded Shared Mutable”. His post clearly explains why the Rust language wishes to steer developers towards RefCell for shared references over use of Cell, its inflexible shared, mutable counterpart.
I recently made the observation that many people seem unaware of the full collection of constraint mechanisms available for protecting race safety. Someone sensibly asked for a link to an article that provides a modern, comprehensive review. It turns out that the pickings are very slim; the best I could find is this Wikipedia article on thread safety. It’s accurate, but incomplete. To close that gap, let me take a stab here at more comprehensive treatment.
Is it possible to improve on Rust’s single-owner strategy to support more complex data structures? Before digging into this challenge, let’s summarize the story so far… The Promise and Limitations of Single-Owner Rust’s single-owner memory management is a form of automatic memory management; a garbage collection strategy that is distinct from tracing and reference-counting. Fundamentally, it is an improvement on RAII, which automatically finalizes some defined resource at the end of its defined lexical scope.