Rust has a nice (IMO, ergonomic) way of representing composition through a language feature called "algebraic data types", although it's sometimes called "discriminated unions" or "sum types".
Sum types combined with "pattern matching" is a particularly powerful combination that can make code very easy to write and read, IMO. But not always, so don't take it as an absolute truth. This is one of many articles that explain sum types in Haskell: https://medium.com/@willkurt/why-sum-types-matter-in-haskell...
Statically-typed languages that borrow from functional concepts like Rust, Haskell, F#, Elm and others usually support that. C# has been debating adding support for it (https://github.com/dotnet/csharplang/issues/113), but its community hasn't reached consensus yet. Dynamically-typed languages like Elixir don't benefit the same way from this without sacrificing the flexibility of the language, but in this case they're considering alternative approaches like set-theoretic types: https://elixir-lang.org/blog/2022/10/05/my-future-with-elixi....
Domain Modelling Made Functional
Totally agree: i think folks dont realize F# is modernized OCaml and this is approximately a typed Lisp semantically. Very useful.
The short answer is this: the compiler enforces type-checked unions (called `enum` in Rust) guaranteed to hold one specific type, and requires any `match` block (cf `switch`) checking a value to exhaustively check all possible ones. That’s not the whole story, but it covers about 99.99% of the use cases the OP refers to.