Могу ли я определить свой собственный псевдоним «сильного» типа в Rust?

#rust

Вопрос:

tl;dr в Rust, существует ли «сильный» псевдоним типа (или механизм ввода), такой, что rustc компилятор отклонит (выдаст ошибку) для ошибок, которые могут быть одного и того же базового типа?

Проблема

В настоящее время могут быть определены псевдонимы типов одного и того же базового типа

 type WidgetCounter = usize;
type FoobarTally = usize;
 

Однако компилятор не отклонит (выдаст ошибку или предупреждение), если я ошибочно перепутаю экземпляры двух псевдонимов типов.

 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);
}
 

(Ржавая игровая площадка)

Возможные Решения?

Я надеюсь на что-то вроде дополнительного ключевого слова strong

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

таким образом, предыдущий код при компиляции вызовет ошибку компилятора:

 error[E4444]: mismatched type aliases
 

Или, может быть, есть хитрый трюк с struct s, который позволил бы этого достичь?

Или грузовой модуль, который определяет макрос для достижения этой цели?

Я знаю, что мог бы «взломать» это , используя псевдонимы разных типов чисел, то i32 есть, затем u32 , затем i64 и т. Д. Но это уродливый хак по многим причинам.

Есть ли способ, чтобы компилятор помог мне избежать этих путаниц с псевдонимами пользовательских типов?

Комментарии:

1. вы просите псевдоним не быть псевдонимом ?

2. rust-unofficial.github.io/patterns/patterns/behavioural/…

Ответ №1:

У Ржавчины есть хороший трюк, называемый идиомой нового типа именно для этого. Обернув один элемент в структуру кортежа, вы можете создать оболочку типа «сильный» или «отличный».

Эта идиома также кратко упоминается в разделе структура кортежей в документах Rust.

Ссылка «Идиома нового типа» содержит отличный пример. Вот один из тех типов, которые вы ищете:

 // 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): amp;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(amp;tally);
  tally = return_tally(tally);

  // This is a compile time error.
  // Counter is not compatible with type Tally.
  // print_tally(amp;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);
}
 

Комментарии:

1. В качестве дополнительного преимущества вы можете использовать этот шаблон для реализации внешних признаков на иностранных типах.

2. К сожалению, изменение существующего кода на/с использованием этой идиомы требует добавления/удаления .0 . Было бы неплохо, если .0 бы можно было автоматически вывести структуру кортежа с одним полем («принуждение к отмене структуры кортежа»?), чтобы обеспечить эргономичность type с безопасностью/строгостью типа структуры кортежа.