In Rust I find myself gaining a good bit of type safety without losing ergonomics by wrapping types in a newtype then implementing Deref for them. At first it might seem like a waste, but it prevents accidentally passing the wrong type of thing to a function (e.g. a user UUID as a post UUID).

This and for the use case from the article we will hopefully gain pattern types in Rust soon.

They do not solve every problem that constructive data modeling does but in my opinion a large portion of what actually occurs in everyday programs. Since they are zero-cost I'd say their cost-benefit ratio is pretty good.

Ada and Pascal also had handled the "encode the range in the type" nicely for decades.

I want to point out that, technically, using Deref for this is an anti-pattern, as Deref is intended exclusively for smart pointers. Nothing really wrong with doing this outside of some loss in opacity (and unexpected behaviour if you're writing a library), but it's worth pointing out

I don't really see the issue in providing Deref for a wrapper type like this. Could you elaborate? I'm not trying to gain full encapsulation, just trying to make sure I'm passing the right kind of wrapper, then using it transparently.