Есть ли у ржавчины идиоматический эквивалент F # typedefs?

Я переписываю существующий код моего в Rust 1.6, и я нашел очень удобным в исходном языке ярлык типа typedef. Например, в моей карточной игре у меня есть значение ранга в F #, определяемое как:

type Rank = uint8

Ответ 1

Из раздела " Язык программирования Rust " под названием " Создание синонимов типов с псевдонимами типов":

Rust предоставляет возможность объявить псевдоним типа, чтобы присвоить существующему типу другое имя. Для этого мы используем ключевое слово type. Например, мы можем создать псевдоним Kilometers для i32 следующим образом:

type Kilometers = i32;

Теперь псевдоним Kilometers является синонимом i32; [...], Kilometers не отдельный, новый тип. Значения с типом Kilometers будут обрабатываться так же, как значения типа i32:

type Kilometers = i32;

let x: i32 = 5;
let y: Kilometers = 5;

println!("x + y = {}", x + y);

Там больше, что вы должны прочитать, но это отвечает на вопрос.


Как часть редакционной статьи, я не думаю, что псевдоним типа отлично подходит во многих местах, где люди его используют. Предполагая, что ваш тип Rank представляет собой что-то общее с колодой карт, я бы предложил либо enum либо новый тип. Причина в том, что с псевдонимом типа вы можете сделать что-то вроде этого:

let rank: Rank = 100;

Что бессмысленно для типичной колоды карт. Перечисление является ограниченным набором. Это означает, что вы никогда не сможете создать недействительный Rank:

enum Rank {
    One, Two, Three, Four, Five,
    Six, Seven, Eight, Nine, Ten,
    Jack, Queen, King, Ace,
}

impl Rank {
    fn from_value(v: u8) -> Result<Rank, ()> {
        use Rank::*;

        let r = match v {
            1 => One,
            2 => Two,
            // ...
            _ => return Err(()),
        };
        Ok(r)
    }

    fn value(&self) -> u8 {
        use Rank::*;

        match *self {
            One => 1,
            Two => 2,
            // ...
        }
    }
}

Новый тип - это просто тип оболочки. Он не потребляет дополнительного пространства по сравнению с переносимым типом, он просто предоставляет фактический новый тип, который позволяет реализовывать методы, которые могут ограничиваться допустимыми значениями. Возможно создать недопустимые значения, но только в пределах вашего собственного кода, а не всего клиентского кода:

struct Rank(u8);

impl Rank {
    fn from_value(v: u8) -> Result<Rank, ()> {
        if v >= 1 && v <= 14 {
            Ok(Rank(v))
        } else {
            Err(())
        }
    }

    fn value(&self) -> u8 {
        self.0
    }
}

Я склонен использовать псевдонимы типов как быстрые заполнители типов. При написании приведенных выше примеров, я на самом деле написал:

type Error = ();

И вернул Result<Rank, Error>, но потом подумал, что это сбивает с толку. :-)

Другой случай, который я использую, - это укоротить шрифт большего размера, который я не хочу скрывать. Это происходит с такими типами, как итераторы или Result, которые вы можете увидеть в стандартной библиотеке. Что-то вроде:

type CardResult<T> = Result<T, Error>;

fn foo() -> CardResult<String> {
    // ..
}