#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
с безопасностью/строгостью типа структуры кортежа.