5 ms·
TypeScript-style untagged unions don't exist in (safe) Rust. Instead, "or" is done with enums (which are tagged/discriminated unions—https://en.wikipedia.org/wi
by matt_kantor 6y ago
TypeScript-style untagged unions don't exist in (safe) Rust. Instead, "or" is done with enums (which are tagged/discriminated unions—https://en.wikipedia.org/wiki/Tagged_union https://en.wikipedia.org/wiki/Tagged_union). One difference is that in TypeScript `number | number` reduces to just `number`, but Rust enums don't work that way (`enum Foo { A(f64), B(f64) }` has two distinct variants).
Check out these equivalent-ish programs:
TypeScript: https://www.typescriptlang.org/play?#code/C4TwDgpgBA6gTgQzJOAeAKgPigXigbwCgoSoA3BAGwFcIAuKdQgX0MIDNqA7AY2AEsA9lyjBBAIUGDKEBFwAUADwbwkKVACMpMuVAA+ULtQC2GiHEwBKBlumyRRUlH7so80JEGvFAOgo1oHCCoACIjU3MQywJiJ1I4CGBqOBFff1ooAEJggAZYkmYoCEoAZ2hHOJIEpJSoNKpafKhWVg5uPiERbgB3RDAMTCUVPvUsa0YYp2rk1L8GiBY2HmES4CgEXAJyeYYARh8AJgBmZsJlrlWoDU38bYCGYDgM1jFJOzl5BEtCV+17eQ033Olx4NzutAYIW6AAsEMAIGRIqcen15DxLEA https://www.typescriptlang.org/play?#code/C4TwDgpgBA6gTgQzJO...
type Wrapper<T> = {
value: T
}
function toBoolean(x: Wrapper<boolean | number>): boolean {
if (typeof x.value === "number") {
return x.value !== 0
} else {
return x.value
}
}
function unwrap<T>(x: Wrapper<T>): T {
return x.value
}
const a = { value: 1.23 }
const b = { value: true }
toBoolean(a)
toBoolean(b)
const c = { value: "whatever" }
unwrap(c)
Rust: https://play.rust-lang.org/?version=stable&edition=2018&gist=57df29387c57028e0e6c60a292835d9f https://play.rust-lang.org/?version=stable&edition=2018&gist...
struct Wrapper<T> {
value: T,
}
enum BooleanOrNumber {
Boolean(bool),
Number(f64),
}
fn to_bool(x: Wrapper<BooleanOrNumber>) -> bool {
match x.value {
BooleanOrNumber::Boolean(b) => b,
BooleanOrNumber::Number(n) => n != 0.0,
}
}
fn unwrap<T>(x: Wrapper<T>) -> T {
x.value
}
fn main() {
let a = Wrapper { value: BooleanOrNumber::Number(1.23) };
let b = Wrapper { value: BooleanOrNumber::Boolean(true) };
to_bool(a);
to_bool(b);
let c = Wrapper { value: "whatever" };
unwrap(c);
}
In Rust you also have traits to play with: https://play.rust-lang.org/?version=stable&edition=2018&gist=f888e94f4ce9d7b94d0cd3578946a12d https://play.rust-lang.org/?version=stable&edition=2018&gist...
struct Wrapper<T> {
value: T,
}
trait Boolable {
fn to_bool(self) -> bool;
}
impl Boolable for bool {
fn to_bool(self) -> bool {
self
}
}
impl Boolable for f64 {
fn to_bool(self) -> bool {
self != 0.0
}
}
fn to_bool(x: Wrapper<impl Boolable>) -> bool {
x.value.to_bool()
}
fn unwrap<T>(x: Wrapper<T>) -> T {
x.value
}
fn main() {
let a = Wrapper { value: 1.23 };
let b = Wrapper { value: true };
to_bool(a);
to_bool(b);
let c = Wrapper { value: "whatever" };
unwrap(c);
}
I find there's a decent mental shift switching between TypeScript and Rust, despite some superficial similarities. I use both languages in different codebases and it usually takes me a bit to adjust when hopping back and forth. Both type systems have useful worldviews, but they're pretty different, and they nudge you towards different ways of structuring your code.