Перейти к содержанию

Типы

Все значения в Mojo имеют соответствующий тип данных. Большинство типов являются номинальными типами, определяемыми структурой. Эти типы являются номинальными (или "именованными"), поскольку равенство типов определяется именем типа, а не его структурой.

Существуют некоторые типы, которые не определены как структуры:

  • Функции типизируются на основе их сигнатур.
  • NoneType - это тип с одним экземпляром, объектом None, который используется для обозначения "нет значения".

Mojo поставляется со стандартной библиотекой, которая предоставляет ряд полезных типов и служебных функций. Эти стандартные типы не имеют привилегий. Каждый из типов стандартной библиотеки определяется точно так же, как пользовательские типы - даже такие базовые типы, как Int и String. Но эти стандартные библиотечные типы являются строительными блоками, которые вы будете использовать для большинства программ Mojo.

Наиболее распространенные типы - это встроенные типы, которые всегда доступны и не требуют импорта. К ним относятся типы для числовых значений, строк, логических значений и другие.

Стандартная библиотека также включает в себя множество других типов, которые вы можете импортировать по мере необходимости, включая типы коллекций, утилиты для взаимодействия с файловой системой и получения системной информации и так далее.

Числовые типы

Основным числовым типом Mojo является Int, который представляет собой целое число со знаком наибольшего размера, поддерживаемого системой — обычно 64 или 32 бита.

Mojo также имеет встроенные типы для целых чисел, целых чисел без знака и значений с плавающей запятой различной точности:

Type name Description
Int8 8-bit signed integer
UInt8 8-bit unsigned integer
Int16 16-bit signed integer
UInt16 16-bit unsigned integer
Int32 32-bit signed integer
UInt32 32-bit unsigned integer
Int64 64-bit signed integer
UInt64 64-bit unsigned integer
Int128 128-bit signed integer
UInt128 128-bit unsigned integer
Int256 256-bit signed integer
UInt256 256-bit unsigned integer
Float16 16-bit floating point number (IEEE 754-2008 binary16)
Float32 32-bit floating point number (IEEE 754-2008 binary32)
Float64 64-bit floating point number (IEEE 754-2008 binary64)
Таблица 1. Числовые типы с определенной точностью

Все типы в таблице 1 на самом деле являются псевдонимами одного типа, SIMD, который рассматривается ниже.

Все числовые типы поддерживают обычные числовые и побитовые операторы. Математический модуль предоставляет ряд дополнительных математических функций.

Вы можете задаться вопросом, когда использовать Int, а когда - другие целочисленные типы. В целом, Int - это надежный вариант по умолчанию, когда вам нужен целочисленный тип и не требуется определенная разрядность. Использование Int в качестве целочисленного типа по умолчанию для API делает API более согласованными и предсказуемыми.

Целые числа со знаком и без знака

Mojo поддерживает как целые числа со знаком (Int), так и беззнаковые (UInt). Вы можете использовать обычные типы Int или UInt, если вам не требуется определенная разрядность. Обратите внимание, что любой псевдоним для типа с фиксированной точностью будет иметь тип SIMD.

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

Тип UInt в Mojo представляет собой целое число без знака, размер которого равен 64 битам для 64-разрядных процессоров и 32 битам для 32-разрядных процессоров. Если вы хотите использовать целое число без знака фиксированного размера, вы можете использовать UInt8, UInt16, UInt32 или UInt64, которые являются псевдонимами для типа SIMD.

Целые числа со знаком и без знака одинаковой разрядности могут представлять одинаковое количество значений, но иметь разные диапазоны. Например, Int8 может представлять 256 значений в диапазоне от -128 до 127. UInt8 также может представлять 256 значений, но представляет диапазон от 0 до 255.

Целые числа со знаком и без знака также по-разному реагируют на переполнение. Когда целое число со знаком выходит за пределы диапазона значений, которые может представлять его тип, значение переходит в отрицательные числа. Например, добавление 1 к var si: Int8 = 127 приводит к значению -128.

Когда целое число без знака выходит за пределы диапазона значений, которые может представлять его тип, значение становится равным нулю. Итак, добавление 1 к var ui: UInt8 = 255 равно 0.

Числа с плавающей запятой

Типы чисел с плавающей запятой представляют действительные числа. Поскольку не все действительные числа могут быть выражены конечным числом битов, числа с плавающей запятой не могут точно представлять каждое значение.

Типы с плавающей запятой, перечисленные в таблице 1 - Float64, Float32 и Float16 — соответствуют стандарту IEEE 754-2008 для представления значений с плавающей запятой. Каждый тип включает в себя знаковый бит, один набор битов, представляющий экспоненту, и другой набор битов, представляющий дробь или мантиссу. В таблице 2 показано, как каждый из этих типов представлен в памяти.

Type name Sign Exponent Mantissa
Float64 1 bit 11 bits 52 bits
Float32 1 bit 8 bits 23 bits
Float16 1 bit 5 bits 10 bits
Таблица 2. Подробная информация о типах чисел с плавающей запятой

Числа со значениями экспоненты, равными всем единицам или всем нулям, представляют собой специальные значения, позволяющие числам с плавающей запятой представлять бесконечность, отрицательную бесконечность, нули со знаком и не-число (NaN). Более подробную информацию о том, как представлены числа, смотрите в IEEE 754 в Википедии.

Несколько моментов, на которые следует обратить внимание при работе со значениями с плавающей запятой:

  • Ошибки округления. Округление может привести к неожиданным результатам. Например, 1/3 не может быть точно представлено в этих форматах с плавающей запятой. Чем больше операций вы выполняете с числами с плавающей запятой, тем больше ошибок округления накапливается.

  • Пробел между последовательными числами. Расстояние между последовательными числами может варьироваться в зависимости от формата чисел с плавающей запятой. Для чисел, близких к нулю, расстояние между последовательными числами очень мало. Для больших положительных и отрицательных чисел интервал между последовательными числами больше 1, поэтому может оказаться невозможным представить последовательные целые числа.

Поскольку значения являются приблизительными, редко бывает полезно сравнивать их с помощью оператора равенства (==). Рассмотрим следующий пример:

var big_num = 1.0e16
var bigger_num = big_num+1.0
print(big_num == bigger_num)
True

Операторы сравнения (< >= и т.д.) работают с числами с плавающей запятой. Вы также можете использовать функцию math.isclose(), чтобы сравнить, равны ли два числа с плавающей запятой в пределах заданного допуска.

Числовые литералы

В дополнение к этим числовым типам стандартные библиотеки предоставляют литералы с целыми числами и плавающей запятой, IntLiteral и FloatLiteral.

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

В таблице 3 приведены краткие форматы, которые вы можете использовать для представления чисел.

Format Examples Notes
Integer literal 1760 Целочисленный литерал в десятичном формате.
Hexadecimal literal 0xaa, 0xFF Целочисленный литерал в шестнадцатеричном формате. Шестнадцатеричные цифры не учитывают регистр.
Octal literal 0o77 Целочисленный литерал в восьмеричном формате.
Binary literal 0b0111 Целочисленный литерал в двоичном формате.
Floating-point literal 3.14, 1.2e9 Литерал с плавающей запятой. Должен содержать десятичную точку, которая будет интерпретироваться как значение с плавающей запятой.
Таблица 3. Числовые литеральные форматы

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

Во время выполнения значения преобразуются в типы конечной точности - Int для целых значений и Float64 для значений с плавающей запятой. (Этот процесс преобразования значения, которое может существовать только во время компиляции, в значение во время выполнения называется материализацией.)

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

var arbitrary_precision = 3.0 * (4.0 / 3.0 - 1.0)
# используйте переменную, чтобы принудительно выполнить следующее вычисление во время выполнения
var three = 3.0
var finite_precision = three * (4.0 / three - 1.0)
print(arbitrary_precision, finite_precision)
1.0 0.99999999999999978

SIMD и DType

Для поддержки высокопроизводительной числовой обработки Mojo использует тип SIMD в качестве основы для своих числовых типов. SIMD (single instruction, multiple data) - это процессорная технология, которая позволяет выполнять операцию над целым набором операндов одновременно. SIMD-тип Mojo абстрагирует SIMD-операции. Значение SIMD представляет собой SIMD—вектор, то есть массив значений фиксированного размера, который может помещаться в регистр процессора. SIMD-векторы определяются двумя параметрами:

  • Значение DType, определяющее тип данных в векторе (например, 32-разрядные числа с плавающей запятой).
  • Количество элементов в векторе, которое должно быть в степени двойки.

Например, вы можете определить вектор из четырех значений Float32 следующим образом:

var vec = SIMD[DType.float32, 4](3.0, 2.0, 2.0, 1.0)

Математические операции над значениями SIMD применяются поэлементно, к каждому отдельному элементу вектора. Например:

var vec1 = SIMD[DType.int8, 4](2, 3, 5, 7)
var vec2 = SIMD[DType.int8, 4](1, 2, 3, 4)
var product = vec1 * vec2
print(product)
[2, 6, 15, 28]

Скалярные значения

Модуль SIMD определяет несколько псевдонимов типов, которые являются сокращениями для различных типов SIMD-векторов. В частности, скалярный тип - это просто SIMD-вектор с одним элементом. Числовые типы, перечисленные в таблице 1, такие как Int8 и Float32, на самом деле являются псевдонимами типов для различных типов скалярных значений:

alias Scalar = SIMD[size=1]
alias Int8 = Scalar[DType.int8]
alias Float32 = Scalar[DType.float32]

Поначалу это может показаться немного запутанным, но это означает, что независимо от того, работаете ли вы с одним значением Float32 или с вектором значений float32, математические операции выполняются точно по одному и тому же пути кода.

Тип DType

Структура DType описывает различные типы данных, которые может содержать SIMD-вектор, и определяет ряд служебных функций для работы с этими типами данных. Структура DType определяет набор псевдонимов, которые действуют как идентификаторы для различных типов данных, таких как DType.int8 и DType.float32. Эти псевдонимы используются при объявлении SIMD-вектора:

var v: SIMD[DType.float64, 16]

Обратите внимание, что DType.float64 - это не тип, а значение, описывающее тип данных. Вы не можете создать переменную с типом DType.float64. Вы можете создать переменную с типом SIMD[DType.float64, 1] (или Float64, что одно и то же).

from utils.numerics import max_finite, min_finite

def describeDType[dtype: DType]():
    print(dtype, "is floating point:", dtype.is_floating_point())
    print(dtype, "is integral:", dtype.is_integral())
    print("Min/max finite values for", dtype)
    print(min_finite[dtype](), max_finite[dtype]())

describeDType[DType.float32]()
float32 is floating point: True
float32 is integral: False
Min/max finite values for float32
-3.4028234663852886e+38 3.4028234663852886e+38

В стандартной библиотеке есть несколько других типов данных, которые также используют абстракцию DType.

Преобразование числового типа

Конструкторы и неявное преобразование описывают обстоятельства, при которых Mojo автоматически преобразует значение из одного типа в другой. Важно отметить, что числовые операторы автоматически не сужают или не расширяют операнды до общего типа.

Вы можете явно преобразовать значение SIMD в другой тип SIMD, либо вызвав его метод cast(), либо передав его в качестве аргумента конструктору целевого типа. Например:

simd1 = SIMD[DType.float32, 4](2.2, 3.3, 4.4, 5.5)
simd2 = SIMD[DType.int16, 4](-1, 2, -3, 4)
simd3 = simd1 * simd2.cast[DType.float32]()  # преобразование метотодом cast()
print("simd3:", simd3)
simd4 = simd2 + SIMD[DType.int16, 4](simd1)  # преобразование конструктором SIMD
print("simd4:", simd4)
simd3: [-2.2, 6.6, -13.200001, 22.0]
simd4: [1, 5, 1, 9]

Вы можете преобразовать скалярное значение, передав его в качестве аргумента конструктору целевого типа. Например:

var my_int: Int16 = 12                 # SIMD[DType.int16, 1]
var my_float: Float32 = 0.75           # SIMD[DType.float32, 1]
result = Float32(my_int) * my_float    # Результат SIMD[DType.float32, 1]
print("Result:", result)
Result: 9.0

Вы можете преобразовать скалярное значение любого числового типа в Int, передав это значение методу конструктора Int(). Кроме того, вы можете передать экземпляр любой структуры, реализующей трейт Intable или IntableRaising, конструктору Int(), чтобы преобразовать этот экземпляр в Int.

Вы можете преобразовать значение Int или IntLiteral в тип UInt, передав значение в конструктор UInt(). Вы не можете преобразовать другие числовые типы в UInt напрямую, хотя вы можете сначала преобразовать их в Int, а затем в UInt.

Строки

Строковый тип Mojo представляет собой изменяемую строку. (Для программистов на Python обратите внимание, что он отличается от стандартной строки Python, которая является неизменяемой) Строки поддерживают множество операторов и распространенных методов.

var s: String = "Testing"
s += " Mojo strings"
print(s)
Testing Mojo strings

Большинство стандартных библиотечных типов соответствуют трейту Stringable, который представляет тип, который может быть преобразован в строку. Используйте String(значение) для явного преобразования значения в строку:

var s = "Items in list: " + String(5)
print(s)
Items in list: 5

Или используйте String.write, чтобы принимать переменные строковые типы, поэтому вам не нужно вызывать String() для каждого значения:

var s = String("Items in list: ", 5)
print(s)
Items in list: 5

Строковые литералы

Как и в случае с числовыми типами, стандартная библиотека включает строковый литеральный тип, используемый для представления строк-литералов в исходном коде программы. Строковые литералы заключаются в одинарные или двойные кавычки.

Соседние литералы объединяются вместе, поэтому вы можете определить длинную строку, используя последовательность литералов, разбитых на несколько строк:

alias s = "A very long string which is "
        "broken into two literals for legibility."

Чтобы определить многострочную строку, заключите литерал в три одинарные или двойные кавычки:

alias s = """
Multi-line string literals let you
enter long blocks of text, including
newlines."""

Обратите внимание, что форма тройных и двойных кавычек также используется для строк документации API.

StringLiteral преобразуется в строку при использовании во время выполнения:

alias param = "foo"        # type = StringLiteral
var runtime_value = "bar"  # type = String
var runtime_value2 = param # type = String

Логические значения

Тип Bool в Mojo представляет логическое значение. Он может принимать одно из двух значений: True или False. Вы можете отменить логическое значение, используя оператор not.

var conditionA = False
var conditionB: Bool
conditionB = not conditionA
print(conditionA, conditionB)
False True

Многие типы имеют логическое представление. Любой тип, реализующий свойство Boolable, имеет логическое представление. Как правило, коллекции оцениваются как True, если они содержат какие-либо элементы, и False, если они пустые; строки оцениваются как True, если они имеют ненулевую длину.

Кортежи(Tuple)

Тип Tuple Mojo представляет собой неизменяемый кортеж, состоящий из нуля или более значений, разделенных запятыми. Кортежи могут состоять из нескольких типов, и вы можете индексировать их несколькими способами.

# Кортежи являются неизменяемыми и могут содержать несколько типов
example_tuple = Tuple[Int, String](1, "Example")

# Назначьте несколько переменных одновременно
x, y = example_tuple
print(x, y)

# Получение отдельных значений с помощью индекса
s = example_tuple[1]
print(s)
1 Example
Example

Вы также можете создать кортеж без явного ввода текста.

example_tuple = (1, "Example")
s = example_tuple[1]
print(s)
Example

При определении функции вы можете явно объявить тип элементов кортежа одним из двух способов:

def return_tuple_1() -> Tuple[Int, Int]:
    return Tuple[Int, Int](1, 1)

def return_tuple_2() -> (Int, Int):
    return (2, 2)

Коллекции

Стандартная библиотека Mojo также включает набор базовых типов коллекций, которые можно использовать для создания более сложных структур данных:

  • List - массив элементов динамического размера.
  • Dict - ассоциативный массив пар ключ-значение.
  • Set - неупорядоченная коллекция уникальных элементов.
  • Optional представляет собой значение, которое может присутствовать, а может и не присутствовать.

Типы коллекций являются универсальными: хотя данная коллекция может содержать только значения определенного типа (например, Int или Float64), вы указываете тип во время компиляции с помощью параметра. Например, вы можете создать список значений типа Int следующим образом:

var l: List[Int] = [1, 2, 3, 4]
# l.append(3.14) # ошибка: FloatLiteral не может быть преобразован в Int

Вам не всегда нужно указывать тип явно. Если Mojo может определить тип, вы можете его не указывать. Например, когда вы создаете список из набора целочисленных литералов, Mojo создает List[Int].

# Inferred type == List[Int]
var l1 = [1, 2, 3, 4]

Там, где вам нужна более гибкая коллекция, тип Variant может содержать значения разных типов. Например, Variant[Int32, Float64] может содержать значение Int32 или Float64 в любой момент времени. (Использование Variant в этом разделе не рассматривается, дополнительную информацию смотрите в документации по API)

В следующих разделах дается краткое введение в основные типы коллекций.

List

List - это массив элементов динамического размера. Элементы списка должны соответствовать свойствам Copyable и Movable. Большинство стандартных библиотечных примитивов, таких как Int, String и SIMD, соответствуют этому трейту. Вы можете создать список, передав тип элемента в качестве параметра, например, следующим образом:

var l = List[String]()

Тип List поддерживает подмножество API Python list, включая возможность добавления в список, извлечения элементов из списка и доступа к элементам списка с использованием индексной записи.

var list = [2, 3, 5]
list.append(7)
list.append(11)
print("Popping last item from list: ", list.pop())
for idx in range(len(list)):
      print(list[idx], end=", ")
Popping last item from list:  11
2, 3, 5, 7,

Обратите внимание, что в предыдущем примере кода при создании списка не был указан тип параметра. Поскольку список создается с набором значений Int, Mojo может определить тип по аргументам.

  • Mojo поддерживает литералы list, set и dictionary для инициализации коллекции:
    # List literal, тип элемента которого равен Int.
    var nums = [2, 3, 5]
    
    Вы также можете использовать явный тип, если вам нужен определенный тип элемента:
    var list : List[UInt8] = [2, 3, 5]
    
    Вы также можете использовать список "понимания" для компактной условной инициализации:
    var list2 = [x*Int(y) for x in nums for y in list if x != 3]
    
  • Вы не можете напечатать список или преобразовать его непосредственно в строку.

    # Не работает
    print(list)
    
    Как показано выше, вы можете печатать отдельные элементы в списке, если они имеют строковый тип.

  • Повторение списка возвращает неизменяемую ссылку на каждый элемент:

var list = [2, 3, 4]
for item in list:
    print(item, end=", ")
2, 3, 4,

Если вы хотите изменить элементы списка, запишите ссылку на элемент с помощью ref вместо того, чтобы создавать копию:

var list = [2, 3, 4]
for ref item in list:     # Захват ссылки на элемент списка
      print(item, end=", ")
      item = 0  # Изменяет элемент внутри списка
print("\nAfter loop:", list[0], list[1], list[2])
2, 3, 4,
After loop: 0 0 0

Вы можете видеть, что исходные записи цикла были изменены.

Dict

Тип Dict представляет собой ассоциативный массив, содержащий пары ключ-значение. Вы можете создать Dict, указав тип ключа и тип значения в качестве параметров и используя словарные литералы:

# Пустой словарь
var empty_dict: Dict[String, Float64] = {}

# Словарь с начальными парами ключ-значение
var values: Dict[String, Float64] = {"pi": 3.14159, "e": 2.71828}

Вы также можете использовать синтаксис конструктора:

var values = Dict[String, Float64]()

Тип ключа в словаре должен соответствовать трейту KeyElement, а элементы value должны соответствовать трейтам Copyable и Movable.

Вы можете вставлять и удалять пары ключ-значение, обновлять значение, присвоенное ключу, и выполнять итерацию по ключам, значениям или элементам в словаре.

Все итераторы Dict выдают ссылки, которые по умолчанию копируются в объявленное имя, но вы можете использовать маркер ref, чтобы избежать копирования:

var d: Dict[String, Float64] = {
    "plasticity": 3.1,
    "elasticity": 1.3,
    "electricity": 9.7
}
for item in d.items():
    print(item.key, item.value)
plasticity 3.1000000000000001
elasticity 1.3
electricity 9.6999999999999993

В данном случае это неизмеримая микрооптимизация, но она полезна при работе с типами, которые не Copyable.

Set

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

Наборы являются универсальными, и тип элемента должен соответствовать трейту KeyElement. Подобно спискам и словарям, наборы поддерживают стандартный литеральный синтаксис, а также понимание генераторами:

i_like = {"sushi", "ice cream", "tacos", "pho"}
you_like = {"burgers", "tacos", "salad", "ice cream"}
we_like = i_like.intersection(you_like)

print("We both like:")
for item in we_like:
    print("-", item)
We both like:
- ice cream
- tacos

Optional

Параметр Optional представляет значение, которое может присутствовать, а может и не присутствовать. Как и другие типы коллекций, он является универсальным и может содержать любой тип, соответствующий трейтам Copyable и Movable.

# Два способа инициализировать необязательный параметр значением
var opt1 = Optional(5)
var opt2: Optional[Int] = 5
# Два способа инициализировать необязательный параметр без значения
var opt3 = Optional[Int]()
var opt4: Optional[Int] = None

Параметр Optional оценивается как True, если он содержит значение, в противном случае значение False. Если параметр Optional содержит значение, вы можете получить ссылку на это значение с помощью метода value(). Но вызов value() для параметра Optional без значения приводит к неопределенному поведению, поэтому вы всегда должны избегать вызова value() внутри условия, которое проверяет, существует ли значение.

var opt: Optional[String] = "Testing"
if opt:
    var value_ref = opt.value()
    print(value_ref)
Testing

В качестве альтернативы вы можете использовать метод or_else(), который возвращает сохраненное значение, если таковое имеется, или указанное пользователем значение по умолчанию в противном случае:

var custom_greeting: Optional[String] = None
print(custom_greeting.or_else("Hello"))

custom_greeting = "Hi"
print(custom_greeting.or_else("Hello"))
Hello
Hi

Типы, передаваемые через регистр, доступные только для памяти и тривиальные типы

В различных местах документации вы увидите ссылки на типы, передаваемые через регистр, доступные только для памяти и тривиальные типы. Типы, передаваемые через регистр и доступные только для памяти, различаются в зависимости от способа хранения данных:

  • Типы, передаваемые через регистр, состоят исключительно из типов данных фиксированного размера, которые (теоретически) могут храниться в машинном регистре. Тип, передаваемый через регистр, может включать в себя другие типы, если они также являются передаваемыми через регистр. Например, Int, Bool и SIMD являются типами, передаваемыми через регистр. Таким образом, структура, передаваемая через регистр, может содержать поля Int и Bool, но не строковое поле. Типы, передаваемые через регистр, объявляются с помощью декоратора @register_passable.

  • Типы, предназначенные только для памяти, состоят из всех других типов, которые специально не обозначены как типы, передаваемые через регистр. Эти типы часто содержат указатели или ссылки на динамически выделяемую память. String, List и Dict - все это примеры типов, доступных только для памяти.

Типы, передаваемые через регистр, имеют несколько иной жизненный цикл, чем типы, доступные только для памяти, который обсуждается в разделе Life of a value.

Существует также ряд незначительных различий в том, как компилятор Mojo обрабатывает типы, передаваемые через регистр, и типы, доступные только для памяти, о которых вам, вероятно, не придется задумываться при программировании на Mojo. Для получения дополнительной информации смотрите ссылку на @register_passable decorator.

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

В дополнение к этим двум категориям, в Mojo также есть "тривиальные" типы. Концептуально тривиальный тип - это просто тип, который не требует какой-либо пользовательской логики в своих методах жизненного цикла. Биты, составляющие экземпляр тривиального типа, могут быть скопированы или перемещены без каких-либо знаний о том, что они делают. В настоящее время тривиальные типы объявляются с использованием декоратора @register_passable(trivial). Тривиальные типы не должны ограничиваться только типами, передаваемыми через регистр, поэтому в будущем мы планируем отделить тривиальные типы от декоратора @register_passable.

AnyType и AnyTrivialRegType

Еще две вещи, которые вы увидите в Mojo API, - это ссылки на AnyType и AnyTrivialRegType. По сути, это метатипы, то есть типы типов.

  • AnyType - это трейт, который представляет тип с деструктором.
  • AnyTrivialRegType - это метатип, представляющий любой тип Mojo, который помечен как тривиальный.

Вы увидите их в подписях, подобных этой:

fn any_type_function[ValueType: AnyTrivialRegType](value: ValueType):
    ...

Вы можете прочитать это так, поскольку any_type_function имеет аргумент value типа ValueType, где ValueType - это тип, передаваемый через регистр, определенный во время компиляции.

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