What we wish to Communicate
Consider the following program fragment:
let x: Box<dyn Trait>;
What can we say about x, and what should we say about x?
I’ll try to jot down answers to the first part here.
1. Existentially quantified
To form a Box<dyn Trait> you must have some object y where typeof(y): Trait which you can then Box::new(y) on.
If we want to be a bit more clear about what is happening with Box<dyn Trait>, we could use this pseudo-Rust:
// "There exists some `T: Trait`"
let x: Box<for<T: Trait> T>;
or in actual Haskell (a larger example):
{-# LANGUAGE ExistentialQuantification, KindSignatures, ConstraintKinds #-}
import Data.Kind
import Control.Monad
-- This is morally equivalent to `dyn`.
data Dyn (c :: * -> Constraint) = forall (t :: *). (c t :: Constraint) => D t
-- Or unannotated:
-- data Dyn c = forall t. c t => D t
-- Morally equivalent to `Vec<Box<dyn Debug>>`.
heteroList :: [Dyn Show]
heteroList = [D (), D 5, D True]
-- A program that prints out each element of the list above
-- to the terminal on a separate line.
main :: IO ()
main = forM_ heteroList $ \(D x) -> print x
Another way to think about existential quantification is that the type variable t is erased here, and hence type erasure.
2. Supports the operations of Trait
We could say:
let x: Box<for<T> T>;
which might be well-formed in our pseudo-Rust, but it would also be useless (we can’t do anything useful with x).
Instead x: Box<dyn Trait> supports all the methods that Trait does.
3. Trait must be object safe
Unlike Haskell (iirc), there are some traits Trait (type classes) which can not be used in dyn Trait and thus existentially quantified and dynamically dispatched. Only those traits that are “object safe” may be used in this way, which means that Trait must satisfy the conditions enumerated in RFC 255.
4. Dynamically dispatched
Rust has two modes of existential quantification, one which uses static dispatch (-> impl Trait) and one which uses dynamic dispatch (dyn Trait). When we have Box<dyn Trait>, a vtable is created for each type T: Trait which contains pointers to the actual implementations of the methods in impl Trait for T (and other details…).
5. Boxed and lives on the heap
Because a Box is involved, the value lives on the heap.
On “dynamic type”
A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to the kinds of values they compute.
[…]
Terms like “dynamically typed” are arguably misnomers and should probably be replaced by “dynamically checked,” but the usage is standard
– Benjamin C. Pierce, Types and Programming Languages
Types do not exist at run-time since type systems are a syntactic method (there’s no syntax in this sense at run-time; you could argue that the bytes that make up the instructions themselves form a syntax, but this is not meaningful). A dynamically checked language like python is statically typed because it only has one type; so it is unityped. At the very least, if we are going to say that python is typed, then the types of python live in the same category as values in a language such as Rust or Haskell.
I believe that calling &(dyn Trait) a dynamic type only leads away from understanding; if we have x : &(dyn Trait), then x clearly has a type, which is &(dyn Trait).
In defense of Jargon
Jargon is a type of language that is used in a particular context and may not be well understood outside that context.
– https://en.wikipedia.org/wiki/Jargon
In any community or any field, it is only natural that there is some terms which are not well understood outside of the field. In Rust, we employ jargon such as:
- lifetime – Great and understandable jargon
- outlives – Inaccurate semantically
- trait object – Not accessible
The goal in communication is to find terms that:
- do not lose semantic content / intent or precision;
- are accessible and easy to comprehend for newcomers to the group and for people it.
Between these points, there exists a natural tension. In the effort to solve 2. you may lose critical information, and in an effort to preserve critical information, you may lose on accessibility. Thus, we should decide what is critical information, and what are details.
The problem is not jargon in general; it is bad jargon. We should try to find terms that strike a good balance between 1. and 2. but also tries to eat the cake and keeps it too.