Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I define my own "strong" type alias in Rust?

Tags:

rust

typing

tl;dr in Rust, is there a "strong" type alias (or typing mechanism) such that the rustc compiler will reject (emit an error) for mix-ups that may be the same underlying type?

Problem

Currently, type aliases of the same underlying type may be defined

type WidgetCounter = usize;
type FoobarTally = usize;

However, the compiler will not reject (emit an error or a warning) if I mistakenly mix up instances of the two type aliases.

fn tally_the_foos(tally: FoobarTally) -> FoobarTally {
    // ...
    tally
}

fn main() {
    let wc: WidgetCounter = 33;
    let ft: FoobarTally = 1;

    // whoops, passed the wrong variable!
    let tally_total = tally_the_foos(wc);
}

(Rust Playground)

Possible Solutions?

I'm hoping for something like an additional keyword strong

strong type WidgetCounter = usize;
strong type FoobarTally = usize;

such that the previous code, when compiled, would cause a compiler error:

error[E4444]: mismatched strong alias type WidgetCounter,
              expected a FoobarTally

Or maybe there is a clever trick with structs that would achieve this?

Or a cargo module that defines a macro to accomplish this?



I know I could "hack" this by type aliasing different number types, i.e. i32, then u32, then i64, etc. But that's an ugly hack for many reasons.


Is there a way to have the compiler help me avoid these custom type alias mixups?

like image 928
JamesThomasMoon Avatar asked Jan 25 '23 05:01

JamesThomasMoon


1 Answers

Rust has a nice trick called the New Type Idiom just for this. By wrapping a single item in a tuple struct, you can create a "strong" or "distinct" type wrapper.

This idiom is also mentioned briefly in the tuple struct section of the Rust docs.

The "New Type Idiom" link has a great example. Here is one similar to the types you are looking for:

// Defines two distinct types. Counter and Tally are incompatible with
// each other, even though they contain the same item type.
struct Counter(usize);
struct Tally(usize);

// You can destructure the parameter here to easily get the contained value.
fn print_tally(Tally(value): &Tally) {
  println!("Tally is {}", value);
}

fn return_tally(tally: Tally) -> Tally {
  tally
}

fn print_value(value: usize) {
  println!("Value is {}", value);
}

fn main() {
  let count: Counter = Counter(12);
  let mut tally: Tally = Tally(10);

  print_tally(&tally);
  tally = return_tally(tally);

  // This is a compile time error.
  // Counter is not compatible with type Tally.
  // print_tally(&count);

  // The contained value can be obtained through destructuring
  // or by potision.
  let Tally(tally_value ) = tally;
  let tally_value_from_position: usize = tally.0;

  print_value(tally_value);
  print_value(tally_value_from_position);
}
like image 160
scupit Avatar answered Jan 26 '23 18:01

scupit