> Well, the rust type system certainly does support contraction, as I can use a reference multiple times. So what is that if not contraction? It seems like rust at least does support contraction for references.
Good question! For shared references, the answer is that they are `Copy`, so they indeed have contraction. Affinity just means that contraction is not a universal property, but some types/propositions may still have contraction.
For mutable references, you can't actually use them multiple times. However, there is a desugaring phase going on before affinity gets checked, so uses of mutable references `r` get replaced by `&mut *r` everywhere. That's not using contraction, it's not literally passing `r` somewhere, it is calling a particular (and interesting) operating on `r` ("reborrowing").
Rust is not just an affine system, it is an affine system extended with borrowing. But I think it is still entirely fair to call it an affine system, for the simple fact that the language will prevent you from "using" a variable twice. "reborrowing" is just not a case of "using", it is its own special case with its own rules.
> But in practice, having absolutely no contraction is not a very useful definition of affine,
Obviously Rust has a class of "duplicable" types, called `Copy`. That's besides the point though.
> If you interpret Rust's type system this way, it's natural to interpret references as exponentials.
Why would that be natural? Mutable references are not even duplicable, so what you say makes little sense for references in general. Maybe you mean shared references -- those are just an example of a duplicable type.
Rust doesn't have a modality in its type system that would make every type duplicable, so there is no equivalent to exponentials. (In particular, `&T` isn't a modality around `T`. It's a different type, with a different representation. And as you noted, even if it were a modality, it wouldn't correspond to exponentials.)
But a type system can be affine/linear without having exponentials so I don't understand the point of this remark.
Uniqueness types seem to be all about how many references there are to a value. You can use linear/affine types to enforce such a uniqueness property (and that is indeed what Rust does), but that doesn't take away from the fact that you have a linear/affine type system.
> Because these systems feel very different to use and enforce very different properties,
I can't talk about the "feel" as I never programmed in an affine language (other than Rust ;), but in terms of the properties, what Rust does is extremely closely related to affine logics: the core property being enforced is that things do not get duplicated. My model of Rust, RustBelt, uses an affine separation logic to encode the properties of the Rust type system, and there's a lot of overlap between separation logic and linear logic. So we have further strong evidence here that it makes perfect sense to call Rust an affine language.